Introduction to GraphQL and Kotlin
GraphQL is a query language for APIs that allows clients to receive only the data they actually need. Unlike REST, where each endpoint returns a fixed structure, GraphQL provides flexibility: a single query can combine data from different sources. Kotlin, in turn, is a modern language for the JVM that combines conciseness and type safety. In this tutorial, we will create a full-featured GraphQL API using Kotlin and the graphql-kotlin-spring-server library.
You will learn how to set up a project, define a schema, write resolvers, and test the API through GraphiQL. Ready? Let's go!
1. Project Setup
1.1. Creating a Spring Boot Application
First, let's create a new project on Spring Initializr. Select:
- Project: Gradle (Kotlin)
- Language: Kotlin
- Spring Boot: 3.2+
- Dependencies: Spring Web, Spring Boot DevTools
After downloading, add the GraphQL dependency to build.gradle.kts:
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 Configuration
Create a file application.yml in src/main/resources:
server: port: 8080
graphql: packages: - "com.example.graphql.demo"This will instruct Spring Boot to scan the com.example.graphql.demo package for resolvers and types.
2. Defining the GraphQL Schema
2.1. Creating the Data Model
Let's create a simple model for a book and an author. In Kotlin, we use data classes:
// src/main/kotlin/com/example/graphql/demo/model/Book.ktpackage 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. Defining the Schema via Code (SDL)
Instead of writing a schema.graphqls file, we will use the @GraphQLDescription and @GraphQLIgnore annotations. The graphql-kotlin library automatically generates the schema based on classes. However, for clarity, let's create a schema.graphqls file in 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. Implementing Resolvers
3.1. Data Storage Service
For simplicity, we will use in-memory storage:
// src/main/kotlin/com/example/graphql/demo/service/LibraryService.ktpackage com.example.graphql.demo.service
import com.example.graphql.demo.model.Authorimport com.example.graphql.demo.model.Bookimport org.springframework.stereotype.Serviceimport java.util.UUID
@Serviceclass LibraryService { private val authors = mutableListOf( Author(id = "1", name = "George Orwell"), Author(id = "2", name = "Ray Bradbury") )
private val books = mutableListOf( Book(id = "1", title = "1984", author = authors[0], year = 1949), Book(id = "2", title = "Fahrenheit 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 getAuthor(id: String): Author? = authors.find { it.id == id }
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 Resolver
Create a resolver for queries:
// src/main/kotlin/com/example/graphql/demo/resolver/QueryResolver.ktpackage com.example.graphql.demo.resolver
import com.example.graphql.demo.model.Authorimport com.example.graphql.demo.model.Bookimport com.example.graphql.demo.service.LibraryServiceimport org.springframework.stereotype.Component
@Componentclass QueryResolver(private val libraryService: LibraryService) {
fun books(): List<Book> = libraryService.getBooks()
fun book(id: String): Book? = libraryService.getBook(id)
fun authors(): List<Author> = libraryService.getAuthors()}3.3. Mutation Resolver
Create a resolver for mutations:
// src/main/kotlin/com/example/graphql/demo/resolver/MutationResolver.ktpackage com.example.graphql.demo.resolver
import com.example.graphql.demo.model.Bookimport com.example.graphql.demo.service.LibraryServiceimport org.springframework.stereotype.Component
@Componentclass MutationResolver(private val libraryService: LibraryService) {
fun addBook(title: String, authorId: String, year: Int): Book { return libraryService.addBook(title, authorId, year) }}4. Testing the API
4.1. Launching the Application
Run the application via the main class or using the command:
./gradlew bootRunAfter starting, the GraphQL endpoint will be available at http://localhost:8080/graphql, and the GraphiQL interface at http://localhost:8080/graphiql.
4.2. Example Queries
Open GraphiQL and execute the following queries:
Getting all books:
query { books { id title author { name } year }}Getting a book by ID:
query { book(id: "1") { title author { name } }}Adding a new book:
mutation { addBook(title: "Brave New World", authorId: "1", year: 1932) { id title author { name } }}Conclusion
We have created a fully functional GraphQL API using Kotlin and Spring Boot. In this tutorial, we covered the basic steps: setting up the project, defining the schema, implementing resolvers, and testing. GraphQL provides powerful capabilities for flexible data retrieval, and Kotlin makes the code concise and safe.
For further study, we recommend exploring subscriptions, error handling, and integration with databases. Good luck with your development!