引言:为什么 GraphQL 和 Kotlin 是现代 API 的完美组合
现代 Web 开发需要灵活且高性能的 API。长期以来作为标准的 REST 常常导致过度获取(服务器返回的数据超出需求)或获取不足(需要多次请求才能获取完整信息)的问题。GraphQL 通过允许客户端在单个查询中仅请求必要数据来解决这些问题。
而 Kotlin 是一种现代、简洁且类型安全的语言,非常适合构建可靠的 backend 服务。借助 Expedia Group 的 graphql-kotlin 库,用 Kotlin 创建 GraphQL API 变得简单直观。在本教程中,我们将逐步创建一个用于管理图书列表的完整 GraphQL API。
你将学习:
- 如何搭建支持 GraphQL 的 Spring Boot 项目。
- 如何使用 Kotlin 类定义 GraphQL schema。
- 如何实现查询(Queries)和变更(Mutations)。
- 如何通过内置的 GraphiQL IDE 测试 API。
第一步:项目搭建和依赖配置
首先,创建一个新的 Spring Boot 项目。我们将使用 Gradle(Kotlin DSL)——这是现代 Kotlin 项目的标准配置。
1.1. 创建项目
你可以使用 Spring Initializr 或手动创建结构。我们需要以下依赖:
- Spring Web —— 用于运行 HTTP 服务器。
- Spring Boot Starter GraphQL —— 核心集成。
- Kotlin Reflection —— 用于 graphql-kotlin 库的正常运行。
1.2. build.gradle.kts 文件
plugins { id("org.springframework.boot") version "3.2.0" id("io.spring.dependency-management") version "1.1.4" kotlin("jvm") version "1.9.20" kotlin("plugin.spring") version "1.9.20"}
group = "com.example"version = "0.0.1-SNAPSHOT"
java { toolchain { languageVersion = JavaLanguageVersion.of(17) }}
repositories { mavenCentral()}
dependencies { implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.boot:spring-boot-starter-graphql") implementation("com.expediagroup:graphql-kotlin-spring-server:7.0.1") implementation("org.jetbrains.kotlin:kotlin-reflect") testImplementation("org.springframework.boot:spring-boot-starter-test")}1.3. application.yml 配置
spring: graphql: graphiql: enabled: true # 启用内置 IDE 用于测试 schema: locations: "classpath:graphql/**/"server: port: 8080第二步:定义 Schema 和数据模型
在 GraphQL 中,schema 是客户端和服务器之间的契约。graphql-kotlin 库允许基于 Kotlin 类自动定义 schema(代码优先方法)。这消除了手动编写 .graphqls 文件的需要。
2.1. 创建数据模型
创建一个简单的 Book 模型和一个用于在内存中存储数据的仓库(Repository)。
package com.example.graphqlkotlin.model
data class Book( val id: String, val title: String, val author: String, val year: Int)2.2. 仓库(内存存储)
package com.example.graphqlkotlin.repository
import com.example.graphqlkotlin.model.Bookimport org.springframework.stereotype.Repositoryimport java.util.concurrent.ConcurrentHashMap
@Repositoryclass BookRepository { private val books = ConcurrentHashMap( mapOf( "1" to Book("1", "War and Peace", "Leo Tolstoy", 1869), "2" to Book("2", "Crime and Punishment", "Fyodor Dostoevsky", 1866)