3、搭建GraphQL服务端:使用Apollo Server快速搭建一个GraphQL服务端

好,咱们直接进入正题。这一章我们要亲手搭一个GraphQL服务端出来。你可能会想:「又要配环境?又要装一堆依赖?」别担心,Apollo Server 把这活儿简化得相当彻底。我当年第一次搭的时候,从零到跑通第一个查询,也就花了十分钟。

3.1 为什么选 Apollo Server?

市面上 GraphQL 服务端的实现有好几种,比如 graphql-yoga、Express GraphQL 等等。但我个人习惯用 Apollo Server,原因很简单:

  • 开箱即用——不需要手动集成 Express 或 Koa
  • 生态成熟——缓存、订阅、联邦等功能都有官方支持
  • 文档清晰——遇到问题查起来不费劲

说白了,Apollo Server 就是 GraphQL 服务端里的「瑞士军刀」。你不需要纠结底层细节,把精力放在业务逻辑上就好。

3.2 初始化项目

先创建一个新目录,然后初始化 npm 项目:

mkdir graphql-server-demo
cd graphql-server-demo
npm init -y

嗯,这里要注意:npm init -y 会生成一个默认的 package.json。如果你想自定义,也可以不加 -y 手动填写。

3.3 安装依赖

接下来安装核心包:

npm install @apollo/server graphql

就两个包?对,就两个。@apollo/server 是 Apollo Server 4.x 的包名,graphql 是 GraphQL 的 JavaScript 参考实现。我在项目中遇到过有人多装了一堆没用的中间件,其实完全没必要。

小提示:如果你用的是 Apollo Server 3.x,包名是 apollo-server。4.x 改成了 @apollo/server,API 也有变化。建议直接用最新的 4.x。

3.4 编写第一个 GraphQL Schema

Schema 是 GraphQL 的核心。它定义了客户端能查什么、怎么查。我们来定义一个简单的图书查询:

// schema.js
const typeDefs = `#graphql
  type Book {
    id: ID!
    title: String!
    author: String!
  }

  type Query {
    books: [Book]
    book(id: ID!): Book
  }
`;

你想想看,这个 Schema 告诉客户端两件事:

  • 有一个 Book 类型,包含 id、title、author 三个字段
  • 有两个查询入口:获取所有图书、根据 ID 获取单本图书

#graphql 这个注释不是随便写的。它告诉代码编辑器(比如 VS Code 的 GraphQL 插件)「下面这段是 GraphQL 语法」,从而获得语法高亮。这个小技巧是我从 Apollo 官方示例里学来的。

3.5 编写 Resolver

Schema 只是「声明」,Resolver 才是「实现」。它告诉 Apollo Server 每个字段的数据从哪里来:

// resolvers.js
const books = [
  { id: '1', title: 'GraphQL 入门', author: '张三' },
  { id: '2', title: 'React 实战', author: '李四' },
];

const resolvers = {
  Query: {
    books: () => books,
    book: (parent, args) => books.find(b => b.id === args.id),
  },
};

这里我用的是内存数据,实际项目中你会换成数据库查询。不过原理一样——Resolver 就是一个函数,返回对应字段的值。

注意:Resolver 的第二个参数 args 包含了客户端传过来的参数。比如查询 book(id: "1") 时,args.id 就是 "1"。我曾经犯过一个低级错误:忘了把 id 转成数字类型,结果死活查不到数据。

3.6 启动服务端

最后一步,把 Schema 和 Resolver 组装起来,启动服务:

// index.js
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { typeDefs } from './schema.js';
import { resolvers } from './resolvers.js';

const server = new ApolloServer({
  typeDefs,
  resolvers,
});

const { url } = await startStandaloneServer(server, {
  listen: { port: 4000 },
});

console.log(`🚀 服务已启动:${url}`);

运行 node index.js,你会看到控制台输出:

🚀 服务已启动:http://localhost:4000/

打开浏览器访问这个地址,你会看到 Apollo Sandbox——一个在线的 GraphQL 查询工具。你可以直接在页面上写查询语句,测试你的 API。

3.7 测试查询

在 Apollo Sandbox 里输入:

query {
  books {
    id
    title
    author
  }
}

点击运行,你会得到:

{
  "data": {
    "books": [
      { "id": "1", "title": "GraphQL 入门", "author": "张三" },
      { "id": "2", "title": "React 实战", "author": "李四" }
    ]
  }
}

看到这个结果,说明你的 GraphQL 服务端已经跑起来了。是不是很简单?

3.8 常见问题与避坑

问题 原因 解决方法
端口被占用 4000 端口已被其他程序使用 修改 listen.port 为其他端口,比如 4001
Schema 语法错误 类型定义中漏了括号或逗号 检查 typeDefs 中的 GraphQL 语法,可以用 graphql 包的 parse 函数验证
Resolver 返回 undefined 字段名拼写错误或数据源中没有该字段 检查 Resolver 函数名是否与 Schema 中的字段名一致
核心要点:Apollo Server 的搭建流程就是「定义 Schema → 编写 Resolver → 启动服务」。记住这个三步走,以后无论项目多复杂,底层逻辑都不会变。

3.9 下一步做什么?

现在你有了一个能跑的服务端。但说实话,这只是冰山一角。接下来我们会:

  • 接入真实数据库(比如 MongoDB 或 PostgreSQL)
  • 处理 Mutation(增删改操作)
  • 添加认证和权限控制

不过别急,先把今天的内容消化掉。自己动手搭一遍,遇到问题别怕——我当年第一次搭的时候,光端口冲突就折腾了半小时。这些都是成长的一部分。

好,这一章就到这儿。下一章我们聊聊「如何用 GraphQL 查询数据」,到时候你会看到客户端怎么跟这个服务端交互。