4. 搭建第一个GraphQL服务
在理解了 GraphQL 的基本概念之后,接下来要真正把服务跑起来。本节的目标是完成一个可运行、可查询、可验证的最小 GraphQL 服务:先选运行时与框架,再初始化项目,然后定义 Schema 与解析器,最后启动服务并用查询验证结果。整个过程围绕一个具体示例展开——一个提供书籍查询的迷你 API。
一、运行时与框架的选择
GraphQL 本身只是一套规范,它规定了查询语言、类型系统和执行语义,但并没有规定用什么语言实现。因此第一步是选择运行时(JavaScript/Node.js、Go、Java、Python 等)和对应的服务框架。对于本课程,我们以 Node.js 运行时为主,同时对比 Go 生态,便于理解不同技术栈的差异。
在 Node.js 生态中,最常用的两个框架是 Apollo Server 和 GraphQL Yoga。Apollo Server 功能完整,生态成熟,内置对缓存、联邦、订阅、错误处理的支持,适合中大型项目;GraphQL Yoga 更轻量,基于 WHATWG Fetch 标准,能直接运行在 Node、Bun、Deno、Cloudflare Workers 等环境,配置简单,适合快速起步和边缘部署。如果使用 Go,则通常选择 gqlgen,它采用 Schema First 方式,通过代码生成保证类型安全,性能好,适合高并发后端服务。
选择时可以按三个维度判断:一是团队已有技术栈,二是部署环境(传统服务器还是 Serverless/边缘),三是是否需要联邦、订阅等高级能力。对于本节的最小示例,我们选择 Apollo Server,因为它文档丰富、启动快,便于观察完整流程。
二、项目初始化
选定 Apollo Server 后,先创建一个空目录并初始化 Node 项目。打开终端执行:
mkdir book-graphql
cd book-graphql
npm init -y
npm install @apollo/server graphql
这里安装了两个包:@apollo/server 是 Apollo Server 的核心实现,graphql 是 GraphQL 的 JavaScript 参考实现,Apollo Server 依赖它来解析和执行查询。安装完成后,在 package.json 中把 "type" 设为 "module",这样可以使用 ES Module 语法。接着创建入口文件 server.js。
三、定义 Schema 与解析器
Schema 是 GraphQL 服务的契约,它声明了客户端可以查询哪些类型、哪些字段。我们定义一个 Book 类型和一个 Query 根类型:
const typeDefs = `#graphql
type Book {
id: ID!
title: String!
author: String!
year: Int
}
type Query {
books: [Book!]!
book(id: ID!): Book
}
`;
其中 ID! 表示非空 ID,[Book!]! 表示非空数组且元素也非空。解析器负责为每个字段提供数据。对于内存示例,我们准备一个数组作为数据源:
const books = [
{ id: '1', title: 'GraphQL 实战', author: '张三', year: 2023 },
{ id: '2', title: 'API 设计之道', author: '李四', year: 2022 },
{ id: '3', title: 'Node.js 进阶', author: '王五', year: 2024 },
];
const resolvers = {
Query: {
books: () => books,
book: (_, { id }) => books.find(b => b.id === id),
},
};
注意 book 解析器接收两个参数:父级返回值(此处为 _)和查询参数 { id }。当客户端传入 id: "2" 时,解析器返回对应书籍对象;若找不到则返回 undefined,GraphQL 会在结果中把该字段置为 null。
四、启动服务并测试
把 Schema 和解析器交给 Apollo Server,并监听端口:
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
const server = new ApolloServer({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, {
listen: { port: 4000 },
});
console.log(`🚀 服务已启动:${url}`);
执行 node server.js,终端输出服务地址 http://localhost:4000/。在浏览器打开该地址,会进入 Apollo Sandbox 界面,可以直接输入查询并执行。例如输入:
query {
books {
title
author
}
}
预期返回:
{
"data": {
"books": [
{ "title": "GraphQL 实战", "author": "张三" },
{ "title": "API 设计之道", "author": "李四" },
{ "title": "Node.js 进阶", "author": "王五" }
]
}
}
也可以用 curl 在命令行验证:
curl -X POST http://localhost:4000/ \
-H "Content-Type: application/json" \
-d '{"query":"{ book(id: \"2\") { title year } }"}'
如果返回的 JSON 中 data.book.title 为 "API 设计之道",且没有 errors 字段,说明 Schema、解析器和 HTTP 层都已正确工作。若出现 errors,常见原因包括:字段名拼写与 Schema 不一致、解析器返回类型与声明类型不匹配、端口被占用等。此时应优先检查终端报错信息和 Sandbox 中的错误提示。