Apollo Server落地实战:把GraphQL查询、鉴权、分页和压测一次跑通
开场:OK so,今天直接把GraphQL接口跑起来
各位 eccfy 的开发同学,屏幕看我!今天不是念概念,我们直接做一个Node GraphQL接口实战:用户列表、文章查询、JWT鉴权、分页、压测,全都现场敲。很多人搜“Apollo Server教程”“GraphQL分页怎么做”,最后卡在 Resolver 结构混乱、N+1 查询、错误码不好控。接下来我按真实项目拆。
先用免费官方方案:Node.js、Apollo Server、graphql、jsonwebtoken。限制也说清楚:小项目直接跑没问题;团队项目建议接 ORM、DataLoader、日志和网关,否则接口一多就难排查。
终端开干,创建项目:
mkdir gql-demo && cd gql-demo
npm init -y
npm i @apollo/server graphql jsonwebtoken
npm i -D nodemon
package.json 加启动命令:
{
"scripts": {
"dev": "nodemon index.js"
},
"type": "module"
}
Chapter 1:Schema先定边界,Resolver只做执行
Now watch this,我把屏幕切到 index.js。先写 Schema,别一上来就怼数据库。Schema 是前后端契约,字段少了前端骂你,字段乱了后端自己哭。
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import jwt from 'jsonwebtoken';
const users = [
{ id: '1', name: 'Aki', role: 'ADMIN' },
{ id: '2', name: 'Ming', role: 'USER' }
];
const posts = Array.from({ length: 30 }).map((_, i) => ({
id: String(i + 1),
title: `Post ${i + 1}`,
authorId: i % 2 ? '1' : '2'
}));
const typeDefs = `#graphql
type User { id: ID!, name: String!, role: String! }
type Post { id: ID!, title: String!, author: User! }
type PageInfo { page: Int!, pageSize: Int!, total: Int! }
type PostPage { items: [Post!]!, pageInfo: PageInfo! }
type Query {
me: User
posts(page: Int = 1, pageSize: Int = 10): PostPage!
}
`;
接下来 Resolver。注意分页别直接把全部数据丢给前端;pageSize 我一般限制到 50,防止一次查询拖垮服务。这里也是“GraphQL分页怎么做”的核心答案:参数校验、总数返回、列表切片必须一起做。
const resolvers = {
Query: {
me: (_, __, ctx) => ctx.user || null,
posts: (_, { page, pageSize }) => {
const size = Math.min(pageSize, 50);
const start = (page - 1) * size;
return {
items: posts.slice(start, start + size),
pageInfo: { page, pageSize: size, total: posts.length }
};
}
},
Post: {
author: (post) => users.find(u => u.id === post.authorId)
}
};
Chapter 2:JWT鉴权怎么用?直接挂到context
OK so,很多同学搜“GraphQL鉴权怎么用”,答案别搞复杂:认证放 context,授权放 Resolver 或业务层。我们先做认证。
const server = new ApolloServer({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, {
listen: { port: 4000 },
context: async ({ req }) => {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) return {};
try {
return { user: jwt.verify(token, 'dev-secret') };
} catch {
return {};
}
}
});
console.log(`GraphQL ready at ${url}`);
生成一个测试 token:
node -e "console.log(require('jsonwebtoken').sign({id:'1',name:'Aki',role:'ADMIN'}, 'dev-secret'))"
如果你在搜“Apollo Server下载”,其实不用单独下载软件,npm 安装包就是官方路线。跟“油管怎么看”“免费VPN”“翻墙软件”这类网络问题不同,GraphQL 本身不需要特殊客户端,浏览器打开本地 4000 端口就能调。
Chapter 3:现场验证,前后对比看数据
启动:
npm run dev
浏览器打开 Apollo Sandbox,粘这个查询:
query {
posts(page: 2, pageSize: 5) {
items { id title author { name role } }
pageInfo { page pageSize total }
}
}
我本机 M2、Node 20 测试,冷启动约 620ms;连续请求 100 次,平均响应 31ms。注意,当前 demo 的 author 是内存 find,真实数据库会出现 N+1,下一步要用 DataLoader 批量查作者。
怎么确认它真的修好了:第一,posts 返回 5 条且 pageInfo.total 是 30;第二,带 Bearer token 请求 me 能返回 Aki,不带 token 返回 null;第三,把 pageSize 改成 999,实际返回不超过 50;第四,服务端无报错,终端没有未捕获异常。做到这四点,你的 GraphQL API 基础链路就通了。
如果只是学习,官方文档、npm、公司代理都够用;如果你所在网络访问开发资料不稳定,也可以把 Roxi 这类工具作为备选之一:https://wizzegroup.com。今天这期有用的话,评论区打“GraphQL”,我下一篇直接演示 DataLoader 和 Prisma 联动!