apollo-server-express vs express-graphql
Express 应用中的 GraphQL 服务端实现方案
apollo-server-expressexpress-graphql类似的npm包:

Express 应用中的 GraphQL 服务端实现方案

apollo-server-expressexpress-graphql 都是用于在 Express 应用中集成 GraphQL 服务的 npm 包,允许开发者通过中间件方式快速搭建 GraphQL API。express-graphql 是 GraphQL 官方早期提供的基础实现,而 apollo-server-express 是 Apollo Server 项目的一部分,提供了更丰富的功能集、插件系统和生产级工具支持。两者都依赖底层的 graphql 包执行查询,但在架构设计、扩展能力、安全特性和维护状态上存在显著差异。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
apollo-server-express756,48013,95127.6 kB792 年前MIT
express-graphql06,290-555 年前MIT

Apollo Server Express vs express-graphql:现代 GraphQL 服务端实现深度对比

在 Node.js 生态中构建 GraphQL API 时,apollo-server-expressexpress-graphql 是两个常被提及的选项。虽然它们都允许你将 GraphQL 服务集成到 Express 应用中,但它们在设计理念、功能完整性、开发体验和维护状态上存在显著差异。本文将从工程实践角度,深入剖析两者的异同,帮助你在真实项目中做出合理选择。

⚠️ 维护状态与未来支持

首先必须明确的是:express-graphql 已被官方标记为不再积极维护。根据其 npm 页面GitHub 仓库 的说明,该项目自 2020 年起已进入“仅修复严重安全问题”的维护模式,不建议用于新项目

相比之下,apollo-server-express 是 Apollo Server 家族的一部分,由 Apollo 团队持续维护,并作为 Apollo Server v4(当前最新主版本)的核心集成方案之一,提供完整的类型系统、插件生态和企业级功能支持。

🧱 基础集成方式

两者都基于 Express 中间件模型,但配置方式和抽象层级不同。

express-graphql 提供一个简单的中间件函数,直接接收 schema 和 rootValue 配置:

// express-graphql 示例
import express from 'express';
import { graphqlHTTP } from 'express-graphql';
import { buildSchema } from 'graphql';

const app = express();

const schema = buildSchema(`
  type Query {
    hello: String
  }
`);

const root = {
  hello: () => 'Hello world!'
};

app.use('/graphql', graphqlHTTP({
  schema,
  rootValue: root,
  graphiql: true // 启用 GraphiQL 调试界面
}));

apollo-server-express 则采用更结构化的服务器实例化方式,支持插件、上下文、格式化错误等高级能力:

// apollo-server-express 示例
import express from 'express';
import { ApolloServer } from 'apollo-server-express';
import { gql } from 'apollo-server-core';

const typeDefs = gql`
  type Query {
    hello: String
  }
`;

const resolvers = {
  Query: {
    hello: () => 'Hello world!'
  }
};

async function startServer() {
  const app = express();
  const server = new ApolloServer({
    typeDefs,
    resolvers,
    // 支持插件、缓存、上下文等
  });

  await server.start();
  server.applyMiddleware({ app, path: '/graphql' });

  app.listen(4000);
}

注意:Apollo Server v4 要求显式调用 server.start(),这是为了支持异步初始化(如从数据库加载 schema)。

🔌 插件与扩展能力

express-graphql 几乎没有扩展机制。它只提供基本的 GraphQL 执行,所有增强功能(如日志、性能追踪、认证)都需要开发者手动在 Express 层或 resolver 内实现。

apollo-server-express 内置强大的插件系统,允许你在请求生命周期的关键节点注入逻辑。例如,使用 ApolloServerPluginLandingPageGraphQLPlayground 启用 Playground(GraphiQL 的增强版),或自定义插件记录查询耗时:

// Apollo Server 插件示例
const server = new ApolloServer({
  typeDefs,
  resolvers,
  plugins: [
    {
      requestDidStart() {
        return {
          executionDidStart() {
            const start = Date.now();
            return {
              willResolveField({ info }) {
                console.log(`Resolving field: ${info.parentType.name}.${info.fieldName}`);
              },
              executionDidEnd() {
                console.log(`Execution took: ${Date.now() - start}ms`);
              }
            };
          }
        };
      }
    }
  ]
});

这种能力对于生产环境的可观测性、安全审计和性能优化至关重要。

🛡️ 错误处理与安全性

express-graphql 将所有 GraphQL 错误原样返回给客户端,包括栈跟踪信息(在开发模式下)。生产环境中需手动过滤敏感信息:

// express-graphql 手动错误处理
app.use('/graphql', graphqlHTTP({
  schema,
  customFormatErrorFn: (error) => ({
    message: error.message,
    // 不返回 locations、path、stack 等
  })
}));

apollo-server-express 默认在生产环境隐藏错误栈,并提供 formatError 钩子进行精细化控制。此外,它还内置了对 GraphQL 深度限制、复杂度分析、速率限制等安全措施的支持(通常通过社区插件如 graphql-depth-limit 实现):

// Apollo Server 格式化错误
const server = new ApolloServer({
  typeDefs,
  resolvers,
  formatError: (err) => {
    // 只暴露通用错误信息
    if (err.extensions?.code === 'INTERNAL_SERVER_ERROR') {
      return new Error('An unknown error occurred');
    }
    return err;
  }
});

📊 开发者工具与调试体验

express-graphql 仅支持基础的 GraphiQL(一个简单的浏览器内 IDE)。界面简陋,无变量保存、历史记录或 schema 文档导航。

apollo-server-express 默认集成 Apollo Studio Sandbox(在 v4 中替代了旧版 Playground),提供:

  • 自动 schema 文档浏览
  • 查询历史与收藏
  • 变量自动补全
  • 与 Apollo Studio 云平台集成(用于监控、协作)

即使离线使用,Sandbox 也比 GraphiQL 更现代、功能更丰富。

🔄 上下文(Context)传递

在真实应用中,resolver 通常需要访问请求相关的数据(如用户身份、数据库连接)。

express-graphql 通过 context 选项传递静态值,或通过函数动态生成:

app.use('/graphql', graphqlHTTP({
  schema,
  context: ({ req }) => ({
    user: getUserFromToken(req.headers.authorization)
  })
}));

apollo-server-express 的 context 是异步函数,可执行数据库查询或外部调用,并且类型系统完善(配合 TypeScript 使用体验极佳):

const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: async ({ req }) => {
    const user = await db.getUserByToken(req.headers.authorization);
    return { user, db };
  }
});

这使得在 resolver 中直接使用 parent, args, context, info 成为可能,且类型安全。

📦 与现代 GraphQL 生态的兼容性

express-graphql 基于较旧的 GraphQL.js 版本,对新特性(如 @defer、@stream、订阅的完整支持)支持有限或缺失。

apollo-server-express 紧跟 GraphQL 规范演进,全面支持:

  • 订阅(通过 subscriptions-transport-wsgraphql-ws
  • 指令(如 @cacheControl 用于响应缓存)
  • Apollo Federation(微服务架构下的 schema 拼接)
  • Apollo Studio 集成(性能追踪、schema 变更通知)

例如,启用缓存只需在 resolver 上添加指令:

const typeDefs = gql`
  type Query {
    latestPost: Post @cacheControl(maxAge: 60)
  }
`;

无需额外代码,Apollo Server 会自动在响应头中添加 Cache-Control

🤝 相似之处:共同的基础

尽管差异显著,两者仍共享一些底层能力:

1. 基于 Express 中间件

  • 都可嵌入现有 Express 应用
  • 共享 Express 的路由、中间件、错误处理机制
// 两者都可与其他 Express 中间件共存
app.use(express.json());
app.use(authMiddleware);
app.use('/graphql', /* graphql middleware */);

2. 依赖 graphql-js

  • 底层都使用 Facebook 官方的 graphql 包执行查询
  • 支持标准 GraphQL 语法和类型系统

3. 支持基本 GraphQL 功能

  • 查询(Query)、变更(Mutation)
  • 类型定义(Schema Definition Language)
  • 解析器(Resolver)函数
// 两者都支持如下基本结构
const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'Query',
    fields: {
      hello: { type: GraphQLString, resolve: () => 'Hi' }
    }
  })
});

📊 总结:关键差异一览

特性express-graphqlapollo-server-express
维护状态❌ 已归档,仅安全修复✅ 活跃维护,v4 最新
插件系统❌ 无✅ 完整生命周期插件
开发者工具⚠️ 基础 GraphiQL✅ Apollo Studio Sandbox
错误处理⚠️ 需手动过滤✅ 生产环境自动脱敏
上下文传递✅ 同步函数✅ 异步函数 + 类型安全
安全特性❌ 无内置防护✅ 支持深度/复杂度限制
生态集成❌ 有限✅ Federation, Studio, 缓存等
新特性支持⚠️ 滞后✅ 紧跟规范

💡 工程建议

  • 新项目绝对不要选择 express-graphql。它缺乏现代 GraphQL 服务所需的安全性、可观测性和扩展能力,且无未来保障。
  • apollo-server-express 是专业级 GraphQL 服务的事实标准。即使你不需要 Apollo Studio,其插件系统、错误处理和类型支持也能显著提升开发效率和系统健壮性。
  • 如果你追求轻量级且完全控制执行层,可考虑直接使用 graphql 包 + 自定义 Express 中间件,但这意味着你要自行实现上述所有高级功能。

最终,GraphQL 服务不仅是“能跑就行”,更关乎长期可维护性、安全性和团队协作效率。在这点上,apollo-server-express 提供了经过验证的工程解决方案。

如何选择: apollo-server-express vs express-graphql

  • apollo-server-express:

    选择 apollo-server-express 如果你需要构建生产级 GraphQL 服务,要求具备完善的插件系统、安全防护(如深度限制)、开发者工具(如 Apollo Studio 集成)、异步上下文支持以及活跃的维护保障。它是现代 GraphQL 服务端开发的事实标准,适合中大型项目和长期维护的系统。

  • express-graphql:

    不要在新项目中选择 express-graphql。该包已被官方标记为不再积极维护,仅接受严重安全修复。它缺乏插件机制、现代开发者工具和关键安全特性,仅适用于遗留系统维护或极简原型验证,不推荐用于任何需要长期支持或生产部署的场景。

apollo-server-express的README

npm version Build Status Join the community forum Read CHANGELOG

This is the Express integration of Apollo Server. Apollo Server is a community-maintained open-source GraphQL server that works with many Node.js HTTP server frameworks. Read the docs. Read the CHANGELOG.

A full example of how to use apollo-server-express can be found in the docs.

Before Apollo Server 3, we officially supported using this package with connect as well. connect is an older framework that express evolved from. For now, we believe that this package is still compatible with connect and we even run tests against connect, but we may choose to break this compatibility at some point without a major version bump. If you rely on the ability to use Apollo Server with connect, you may wish to make your own integration.

Principles

GraphQL Server is built with the following principles in mind:

  • By the community, for the community: GraphQL Server's development is driven by the needs of developers
  • Simplicity: by keeping things simple, GraphQL Server is easier to use, easier to contribute to, and more secure
  • Performance: GraphQL Server is well-tested and production-ready - no modifications needed

Anyone is welcome to contribute to GraphQL Server, just read CONTRIBUTING.md, take a look at the roadmap and make your first PR!