Введение в GraphQL и Kotlin
GraphQL — это язык запросов для API, который позволяет клиентам получать только те данные, которые им действительно нужны. В отличие от REST, где каждый эндпоинт возвращает фиксированную структуру, GraphQL дает гибкость: один запрос может объединять данные из разных источников. Kotlin, в свою очередь, — современный язык для JVM, который сочетает лаконичность и безопасность типов. В этом туториале мы создадим полноценный GraphQL API с помощью Kotlin и библиотеки graphql-kotlin-spring-server.
Вы узнаете, как настроить проект, определить схему, написать резолверы и протестировать API через GraphiQL. Готовы? Поехали!
1. Настройка проекта
1.1. Создание Spring Boot приложения
Для начала создадим новый проект на Spring Initializr. Выберите:
- Project: Gradle (Kotlin)
- Language: Kotlin
- Spring Boot: 3.2+
- Dependencies: Spring Web, Spring Boot DevTools
После скачивания добавьте в build.gradle.kts зависимость для GraphQL:
dependencies {
implementation("com.expediagroup:graphql-kotlin-spring-server:7.0.0")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
1.2. Конфигурация приложения
Создайте файл application.yml в src/main/resources:
server:
port: 8080
graphql:
packages:
- "com.example.graphql.demo"
Это укажет Spring Boot сканировать пакет com.example.graphql.demo для поиска резолверов и типов.
2. Определение схемы GraphQL
2.1. Создание модели данных
Создадим простую модель для книги и автора. В Kotlin используем data class:
// src/main/kotlin/com/example/graphql/demo/model/Book.kt
package com.example.graphql.demo.model
data class Book(
val id: String,
val title: String,
val author: Author,
val year: Int
)
data class Author(
val id: String,
val name: String,
val books: List<Book> = emptyList()
)
2.2. Определение схемы через код (SDL)
Вместо написания файла schema.graphqls мы воспользуемся аннотациями @GraphQLDescription и @GraphQLIgnore. Библиотека graphql-kotlin автоматически генерирует схему на основе классов. Однако для ясности создадим файл schema.graphqls в src/main/resources:
type Query {
books: [Book!]!
book(id: String!): Book
authors: [Author!]!
}
type Mutation {
addBook(title: String!, authorId: String!, year: Int!): Book!
}
type Book {
id: String!
title: String!
author: Author!
year: Int!
}
type Author {
id: String!
name: String!
books: [Book!]!
}
3. Реализация резолверов
3.1. Сервис для хранения данных
Для простоты будем использовать in-memory хранилище:
// src/main/kotlin/com/example/graphql/demo/service/LibraryService.kt
package com.example.graphql.demo.service
import com.example.graphql.demo.model.Author
import com.example.graphql.demo.model.Book
import org.springframework.stereotype.Service
import java.util.UUID
@Service
class LibraryService {
private val authors = mutableListOf(
Author(id = "1", name = "Джордж Оруэлл"),
Author(id = "2", name = "Рэй Брэдбери")
)
private val books = mutableListOf(
Book(id = "1", title = "1984", author = authors[0], year = 1949),
Book(id = "2", title = "451° по Фаренгейту", author = authors[1], year = 1953)
)
fun getBooks(): List<Book> = books
fun getBook(id: String): Book? = books.find { it.id == id }
fun getAuthors(): List<Author> = authors
fun addBook(title: String, authorId: String, year: Int): Book {
val author = authors.find { it.id == authorId }
?: throw IllegalArgumentException("Author not found")
val book = Book(id = UUID.randomUUID().toString(), title = title, author = author, year = year)
books.add(book)
return book
}
}
3.2. Query резолверы
Создадим класс для обработки запросов:
// src/main/kotlin/com/example/graphql/demo/resolver/QueryResolver.kt
package com.example.graphql.demo.resolver
import com.example.graphql.demo.model.Author
import com.example.graphql.demo.model.Book
import com.example.graphql.demo.service.LibraryService
import com.expediagroup.graphql.server.operations.Query
import org.springframework.stereotype.Component
@Component
class QueryResolver(private val service: LibraryService) : Query {
fun books(): List<Book> = service.getBooks()
fun book(id: String): Book? = service.getBook(id)
fun authors(): List<Author> = service.getAuthors()
}
3.3. Mutation резолверы
Теперь добавим мутацию для добавления книги:
// src/main/kotlin/com/example/graphql/demo/resolver/MutationResolver.kt
package com.example.graphql.demo.resolver
import com.example.graphql.demo.model.Book
import com.example.graphql.demo.service.LibraryService
import com.expediagroup.graphql.server.operations.Mutation
import org.springframework.stereotype.Component
@Component
class MutationResolver(private val service: LibraryService) : Mutation {
fun addBook(title: String, authorId: String, year: Int): Book {
return service.addBook(title, authorId, year)
}
}
4. Запуск и тестирование
4.1. Запуск приложения
Добавьте главный класс приложения:
// src/main/kotlin/com/example/graphql/demo/GraphqlDemoApplication.kt
package com.example.graphql.demo
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class GraphqlDemoApplication
fun main(args: Array<String>) {
runApplication<GraphqlDemoApplication>(*args)
}
Запустите приложение через Gradle: ./gradlew bootRun.
4.2. Тестирование через GraphiQL
Откройте браузер и перейдите на http://