Introduction: Why GraphQL and Kotlin?
\nIn the world of modern web development, choosing the right approach to building an API can determine the success of an entire project. GraphQL, developed by Facebook, offers a flexible alternative to REST, allowing clients to request only the data they actually need. And Kotlin, a modern programming language for the JVM, adds conciseness, null-safety, and powerful coroutine support to this.
\nIn this tutorial, we will practically build a full-fledged GraphQL API using Kotlin and the graphql-kotlin-spring-server framework. You will learn how to configure a server, define a data schema, write resolvers, and test the API. By the end of the article, you will be able to independently implement GraphQL in your Kotlin projects.
1. Project Setup and Dependencies
\nFirst, let's create a new Spring Boot project with GraphQL support. We will use Gradle as the build system.
\n\n1.1. Creating the Basic Structure
\nCreate a new project in IntelliJ IDEA or via Spring Initializr. Add the following dependencies to build.gradle.kts:
plugins {\n id("org.springframework.boot") version "3.2.0"\n id("io.spring.dependency-management") version "1.1.4"\n kotlin("jvm") version "1.9.21"\n kotlin("plugin.spring") version "1.9.21"\n}\n\ndependencies {\n implementation("org.springframework.boot:spring-boot-starter-web")\n implementation("com.expediagroup:graphql-kotlin-spring-server:7.0.1")\n implementation("org.jetbrains.kotlin:kotlin-reflect")\n implementation("org.jetbrains.kotlin:kotlin-stdlib")\n testImplementation("org.springframework.boot:spring-boot-starter-test")\n}\n\n1.2. Application Configuration
\nCreate an application.yml file in src/main/resources:
server:\n port: 8080\n\ngraphql:\n packages:\n - "com.example.graphqldemo"\n schema:\n introspection-enabled: true\n exception-handlers-enabled: true\nThis is the basic configuration. The introspection-enabled: true parameter allows using GraphiQL for testing.
2. Defining the GraphQL Schema
\nIn graphql-kotlin, the schema is built automatically based on your data classes. This is one of the main advantages — there is no need to write a separate SDL file.
2.1. Creating the Data Model
\nLet's assume we are creating an API for a book library. Let's define the models:
\npackage com.example.graphqldemo.model\n\ndata class Book(\n val id: String,\n val title: String,\n val author: Author,\n val year: Int,\n val genre: String\n)\n\ndata class Author(\n val id: String,\n val name: String,\n val books: List<Book> = emptyList()\n)\n\n// Input types for mutations\ndata class BookInput(\n val title: String,\n val authorId: String,\n val year: Int,\n val genre: String\n)\n\n2.2. Data Service
\nLet's create a simple in-memory service that will store the data:
\npackage com.example.graphqldemo.service\n\nimport com.example.graphqldemo.model.Author\nimport com.example.graphqldemo.model.Book\nimport com.example.graphqldemo.model.BookInput\nimport org.springframework.stereotype.Service\nimport java.util.UUID\n\n@Service\nclass LibraryService {\n private val authors = mutableListOf(\n Author("1", "J.K. Rowling"),\n Author("2", "George Orwell")\n )\n private val books = mutableListOf(\n Book("1", "Harry Potter and the Philosopher's Stone", authors[0], 1997, "Fantasy"),\n Book("2", "1984", authors[1], 1949, "Dystopia")\n )\n\n fun getBooks(): List<Book> = books\n fun getBookById(id: String): Book? = books.find { it.id == id }\n fun getAuthors(): List<Author> = authors\n fun addBook(input: BookInput): Book {\n val author = authors.fin