You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Apollo Server配置不同GraphQL端点区分公私有查询?

嘿,这个问题其实没你想的那么复杂——Apollo Server本身就可以和Express这类Node.js HTTP框架配合,轻松实现多端点的配置,不用被那些单端点示例限制住。我给你一步步拆解实现方案:

核心思路

我们的目标是:

  • /graphql:无需认证,直接处理公共查询
  • /auth/graphql:必须先通过认证,才能处理私有查询

实现的关键是利用HTTP框架的路由中间件能力:给私有端点前置一个认证校验中间件,再分别把Apollo Server挂载到两个不同的路由上。你甚至可以复用同一个Schema和Resolver,只通过上下文(Context)区分用户是否已认证。

具体实现步骤

1. 安装必要依赖

首先确保你装了基础的依赖包:

npm install express apollo-server-express jsonwebtoken

(这里用JWT作为认证方式,你可以根据实际需求换成session或其他方案)

2. 编写认证中间件

先写一个校验用户身份的中间件,比如验证请求头里的JWT令牌:

const jwt = require('jsonwebtoken');

// 认证中间件:验证JWT并将用户信息挂载到请求对象
const authenticateUser = (req, res, next) => {
  const authHeader = req.headers.authorization;
  
  // 检查请求头是否包含有效的Bearer令牌
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ error: '请提供有效的认证令牌' });
  }

  const token = authHeader.split(' ')[1];
  try {
    // 验证令牌并解析用户信息
    const user = jwt.verify(token, process.env.JWT_SECRET);
    req.user = user; // 将用户信息存入req,后续Apollo的Context可以获取
    next(); // 验证通过,继续执行后续中间件
  } catch (err) {
    return res.status(401).json({ error: '令牌无效或已过期' });
  }
};

3. 配置Express服务器与Apollo Server

接下来初始化Express,并把Apollo Server分别挂载到两个路由上。这里有两种方案:

方案一:复用同一个Apollo Server实例(推荐)

这种方式更节省资源,因为不需要创建两个Apollo Server实例:

const express = require('express');
const { ApolloServer } = require('apollo-server-express');
// 导入你的Schema和Resolver(假设你已经定义好了)
const { typeDefs, resolvers } = require('./schema');

const app = express();

// 创建Apollo Server实例,上下文里传入请求对象的用户信息
const apolloServer = new ApolloServer({
  typeDefs,
  resolvers,
  context: ({ req }) => ({
    user: req.user, // 私有路由会有req.user,公共路由则为undefined
  }),
});

// 启动Apollo Server(必须在挂载前调用start())
await apolloServer.start();

// 配置公共端点:直接挂载,不需要认证
app.use('/graphql', apolloServer.getMiddleware());

// 配置私有端点:先经过认证中间件,再挂载Apollo
app.use('/auth/graphql', authenticateUser, apolloServer.getMiddleware());

// 启动服务器
app.listen({ port: 4000 }, () => {
  console.log('✅ 公共端点运行在 http://localhost:4000/graphql');
  console.log('✅ 私有端点运行在 http://localhost:4000/auth/graphql');
});

方案二:创建两个独立的Apollo Server实例

如果你需要给两个端点配置完全不同的Schema或插件,可以用这种方式:

// 公共Apollo Server(无认证)
const publicApollo = new ApolloServer({
  typeDefs: publicTypeDefs, // 可以单独定义公共Schema
  resolvers: publicResolvers,
  context: () => ({ /* 公共上下文,无需用户信息 */ }),
});

// 私有Apollo Server(依赖认证)
const privateApollo = new ApolloServer({
  typeDefs: privateTypeDefs, // 单独定义私有Schema
  resolvers: privateResolvers,
  context: ({ req }) => ({ user: req.user }),
});

// 启动并挂载
await publicApollo.start();
await privateApollo.start();

publicApollo.applyMiddleware({ app, path: '/graphql' });
app.use('/auth/graphql', authenticateUser);
privateApollo.applyMiddleware({ app, path: '/auth/graphql' });

4. 在Resolver中区分认证状态

在Resolver里,你可以通过Context里的user字段判断用户是否已认证,从而控制数据访问:

const resolvers = {
  Query: {
    // 公共查询:所有人都能访问
    publicPosts: () => {
      return ['公共文章1', '公共文章2'];
    },
    // 私有查询:只有认证用户能访问
    userProfile: (_, __, { user }) => {
      if (!user) {
        throw new Error('未认证用户无法访问私有数据');
      }
      return {
        id: user.id,
        username: user.username,
        email: user.email,
      };
    },
  },
};
额外优化建议
  • 可以用GraphQL的Directive(比如@auth)来标记需要认证的字段,配合Apollo Server的插件自动校验,让代码更简洁。
  • 如果用TypeScript,给Context定义类型,能获得更好的类型提示。
  • 可以在私有端点的Apollo Server配置里关闭Playground(如果不需要的话),或者给Playground配置默认的认证令牌,方便调试:
const privateApollo = new ApolloServer({
  // ...其他配置
  playground: {
    settings: {
      'request.credentials': 'include',
    },
    headers: {
      Authorization: 'Bearer YOUR_TEST_TOKEN',
    },
  },
});
注意事项
  • 确保认证中间件在Apollo的中间件之前执行,否则req.user不会被正确设置。
  • 如果用的是Fastify、Koa等其他HTTP框架,思路完全一致:先给私有路由加认证中间件,再挂载Apollo Server。
  • 生产环境中,记得把JWT密钥等敏感信息放在环境变量里,不要硬编码。

内容的提问来源于stack exchange,提问作者timbo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:04:00