GraphQL API с Kotlin: пошаговый туториал на практике

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Почему GraphQL и Kotlin — идеальная пара?

В современной веб-разработке выбор правильного инструмента для создания API определяет скорость разработки и удобство поддержки. GraphQL — это язык запросов, который позволяет клиентам запрашивать только те данные, которые им нужны, избавляя от проблемы over-fetching и under-fetching, свойственной REST. Kotlin, в свою очередь, — современный, лаконичный и безопасный язык, который идеально подходит для бэкенд-разработки, особенно в связке со Spring Boot.

В этом туториале мы на практике создадим полноценный GraphQL API на Kotlin. Вы узнаете, как настроить сервер, определить схему, реализовать запросы и мутации, а также протестировать готовый API. К концу статьи у вас будет работающий проект, который можно использовать как основу для своих приложений.

1. Настройка проекта и зависимости

Первым делом создадим новый проект Spring Boot с поддержкой GraphQL. Мы будем использовать Spring for GraphQL — официальную интеграцию, которая делает работу максимально простой.

1.1. Создание проекта

Вы можете использовать Spring Initializr или любой IDE (IntelliJ IDEA). Добавьте следующие зависимости:

  • Spring Web
  • Spring for GraphQL
  • Spring Data JPA
  • H2 Database (для простоты)

В файле build.gradle.kts должны быть следующие строки:

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-graphql")
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    runtimeOnly("com.h2database:h2")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

1.2. Конфигурация

Создадим простую сущность Book и репозиторий для работы с базой данных.

// Book.kt
@Entity
data class Book(
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long = 0,
    val title: String,
    val author: String,
    val year: Int
)

// BookRepository.kt
interface BookRepository : JpaRepository<Book, Long>

2. Создание GraphQL схемы и контроллера

GraphQL использует строгую типизацию. Схема определяет, какие данные доступны и как их можно запрашивать. В Spring for GraphQL схему можно описать с помощью аннотаций.

2.1. Определение схемы через код

Создадим класс-контроллер, который будет обрабатывать GraphQL запросы. Используем аннотацию @QueryMapping для чтения данных и @MutationMapping для изменений.

// BookController.kt
@Controller
class BookController(private val repository: BookRepository) {

    @QueryMapping
    fun allBooks(): List<Book> = repository.findAll()

    @QueryMapping
    fun bookById(@Argument id: Long): Book? = repository.findById(id).orElse(null)

    @MutationMapping
    fun addBook(@Argument title: String, @Argument author: String, @Argument year: Int): Book {
        val book = Book(title = title, author = author, year = year)
        return repository.save(book)
    }

    @MutationMapping
    fun deleteBook(@Argument id: Long): Boolean {
        if (repository.existsById(id)) {
            repository.deleteById(id)
            return true
        }
        return false
    }
}

2.2. Альтернатива: файл схемы .graphqls

Вы также можете определить схему в отдельном файле schema.graphqls в папке resources/graphql:

type Query {
    allBooks: [Book]
    bookById(id: ID!): Book
}

type Mutation {
    addBook(title: String!, author: String!, year: Int!): Book
    deleteBook(id: ID!): Boolean
}

type Book {
    id: ID!
    title: String!
    author: String!
    year: Int!
}

Оба подхода работают, но файл схемы даёт больше гибкости и наглядности.

3. Написание и тестирование запросов

Теперь, когда сервер настроен, запустим приложение и протестируем его. Spring Boot автоматически поднимает GraphQL эндпоинт по адресу http://localhost:8080/graphql.

3.1. Тестирование через GraphiQL

По умолчанию Spring for GraphQL включает встроенный инструмент GraphiQL. Откройте браузер по адресу http://localhost:8080/graphiql. Вы увидите интерфейс для отправки запросов.

Попробуем добавить книгу:

mutation {
  addBook(title: "Война и мир", author: "Лев Толстой", year: 1869) {
    id
    title
    author
    year
  }
}

Ответ:

{
  "data": {
    "addBook": {
      "id": "1",
      "title": "Война и мир",
      "author": "Лев Толстой",
      "year": 1869
    }
  }
}

3.2. Запрос всех книг

Теперь получим список всех книг, запрашивая только нужные поля:

query {
  allBooks {
    id
    title
    author
  }
}

Обратите внимание: мы не запрашиваем поле year, и сервер его не вернёт. Это и есть главное преимущество GraphQL — клиент сам решает, какие данные получить.

3.3. Поиск по ID

query {
  bookById(id: 1) {
    title
    author
  }
}

4. Расширенные возможности и лучшие практики

Наше базовое приложение готово. Но в реальных проектах часто требуется больше: обработка ошибок, пагинация, подписки (subscriptions) и безопасность.

4.1. Обработка ошибок

Spring for GraphQL автоматически преобразует исключения в GraphQL ошибки. Вы можете кастомизировать это поведение, создав свой DataFetcherExceptionResolver:

@Component
class CustomExceptionResolver : DataFetcherExceptionResolverAdapter() {
    override fun resolveToSingleError(ex: Throwable, env: DataFetchingEnvironment): GraphQLError? {
        return when (ex) {
            is NoSuchElementException -> GraphqlErrorBuilder.newError()
                .message("Книга не найдена")
                .path(env.executionStepInfo.path)
                .build()
            else -> null // передать стандартному                

Блоги

Рекомендаций по книгам