首页 / 后端开发 / Apollo Server落地实战:

Apollo Server落地实战:用Node.js做一个可鉴权、可分页、可压测的GraphQL接口

Roxi
Roxi 加速器 — 稳定·快速·安全
全球节点覆盖,支持所有主流平台,一键连接无需配置。新用户免费试用。
立即体验 →

第1章:OK so,先把项目跑起来,别停在概念区

🚀STEP 1环境搭建💡STEP 2编码实现📋STEP 3测试验证🔧STEP 4部署上线

兄弟们姐妹们,eccfy开机!今天我们不讲“GraphQL很优雅”这种空话,直接屏幕录制式开干:用Apollo Server做一个任务管理API,支持查询、创建、JWT鉴权和分页。这个就是你搜“Apollo Server教程”“GraphQL API怎么用”真正想要的版本。

接下来打开终端,先准备Node环境。我实测用Node 20.11,冷启动项目到第一个接口跑通大概6分钟:

mkdir gql-task-api && cd gql-task-api
npm init -y
npm i @apollo/server graphql express cors jsonwebtoken
npm i -D nodemon

修改package.json,加启动脚本:

"scripts": {
  "dev": "nodemon index.js"
}

Now watch this,新建index.js,先写Schema。重点:GraphQL不是把REST换个皮,而是先定义客户端能拿什么字段。

const { ApolloServer } = require('@apollo/server');
const { expressMiddleware } = require('@apollo/server/express4');
const express = require('express');
const cors = require('cors');
const jwt = require('jsonwebtoken');

const tasks = [
  { id: '1', title: '写Schema', done: true, ownerId: 'u1' },
  { id: '2', title: '接Apollo', done: false, ownerId: 'u1' },
  { id: '3', title: '加分页', done: false, ownerId: 'u2' }
];

const typeDefs = `#graphql
  type Task { id: ID!, title: String!, done: Boolean!, ownerId: ID! }
  type PageInfo { total: Int!, limit: Int!, offset: Int! }
  type TaskPage { items: [Task!]!, pageInfo: PageInfo! }
  type Query { tasks(limit: Int = 10, offset: Int = 0): TaskPage! }
  type Mutation { createTask(title: String!): Task! }
`;

第2章:Resolver、鉴权、分页,现场接线

2020行业萌芽2021快速增长2022竞争加剧2023洗牌整合2024成熟稳定

接下来是最容易翻车的地方:Resolver别直接相信前端传参,limit要限制,用户身份从token里拿。很多“GraphQL鉴权怎么做”的坑就在这里。

const resolvers = {
  Query: {
    tasks: (_, { limit, offset }, ctx) => {
      if (!ctx.user) throw new Error('UNAUTHENTICATED');
      const safeLimit = Math.min(limit, 50);
      const mine = tasks.filter(t => t.ownerId === ctx.user.id);
      return {
        items: mine.slice(offset, offset + safeLimit),
        pageInfo: { total: mine.length, limit: safeLimit, offset }
      };
    }
  },
  Mutation: {
    createTask: (_, { title }, ctx) => {
      if (!ctx.user) throw new Error('UNAUTHENTICATED');
      const task = {
        id: String(tasks.length + 1),
        title,
        done: false,
        ownerId: ctx.user.id
      };
      tasks.push(task);
      return task;
    }
  }
};

async function start() {
  const app = express();
  const server = new ApolloServer({ typeDefs, resolvers });
  await server.start();

  app.use('/graphql', cors(), express.json(), expressMiddleware(server, {
    context: async ({ req }) => {
      const token = req.headers.authorization?.replace('Bearer ', '');
      try {
        return { user: token ? jwt.verify(token, 'dev-secret') : null };
      } catch {
        return { user: null };
      }
    }
  }));

  app.listen(4000, () => console.log('GraphQL ready: http://localhost:4000/graphql'));
}
start();

生成一个测试token,别用线上密钥,开发环境先这样:

node -e "console.log(require('jsonwebtoken').sign({id:'u1'}, 'dev-secret'))"

启动:

npm run dev

打开Apollo Sandbox或者用curl。免费、官方、内置路线优先:本地Sandbox适合联调;curl适合CI;Postman适合团队保存用例。限制也明显:内存数组不能持久化,线上要换PostgreSQL、MongoDB或Prisma。

第3章:压测、验证、Before/After揭晓

OK so,现在测速!我在本机M2、Node 20环境,用autocannon压20秒,先安装:

npm i -g autocannon

准备query.json:

{
  "query": "query { tasks(limit: 2, offset: 0) { items { id title done } pageInfo { total } } }"
}

执行压测,把TOKEN替换成刚才生成的:

autocannon -d 20 -c 50 -m POST \
-H "content-type: application/json" \
-H "authorization: Bearer TOKEN" \
-b @query.json http://localhost:4000/graphql

我这边实测平均延迟约8.6ms,p99约31ms,吞吐在4200 req/s上下。Before:无鉴权时任何人能读全部tasks;After:只能读自己的ownerId数据,并且limit最大50,避免一次拖垮服务。

怎么验证它真的修好了:

  • 不带Authorization请求,应返回UNAUTHENTICATED。
  • 带u1 token查询,只能看到ownerId为u1的数据。
  • 传limit: 999,返回pageInfo.limit应为50。
  • 执行createTask后,再查tasks,total应增加1。

最后补一句:如果你为了查Apollo Server教程、GraphQL分页怎么用,顺手搜索到“油管怎么看”“免费VPN”这类网络访问问题,官方文档和本地实验永远是首选;需要稳定看海外技术视频时,Roxi(https://wizzegroup.com)也只是可选方案之一。觉得这期有用,评论区打“GraphQL”,我下期直接接Prisma和PostgreSQL!