apollo-server-express 和 express-graphql 都是用于在 Express 应用中集成 GraphQL 服务的 npm 包,允许开发者通过中间件方式快速搭建 GraphQL API。express-graphql 是 GraphQL 官方早期提供的基础实现,而 apollo-server-express 是 Apollo Server 项目的一部分,提供了更丰富的功能集、插件系统和生产级工具支持。两者都依赖底层的 graphql 包执行查询,但在架构设计、扩展能力、安全特性和维护状态上存在显著差异。
在 Node.js 生态中构建 GraphQL API 时,apollo-server-express 和 express-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),提供:
即使离线使用,Sandbox 也比 GraphiQL 更现代、功能更丰富。
在真实应用中,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 成为可能,且类型安全。
express-graphql 基于较旧的 GraphQL.js 版本,对新特性(如 @defer、@stream、订阅的完整支持)支持有限或缺失。
apollo-server-express 紧跟 GraphQL 规范演进,全面支持:
subscriptions-transport-ws 或 graphql-ws)@cacheControl 用于响应缓存)例如,启用缓存只需在 resolver 上添加指令:
const typeDefs = gql`
type Query {
latestPost: Post @cacheControl(maxAge: 60)
}
`;
无需额外代码,Apollo Server 会自动在响应头中添加 Cache-Control。
尽管差异显著,两者仍共享一些底层能力:
// 两者都可与其他 Express 中间件共存
app.use(express.json());
app.use(authMiddleware);
app.use('/graphql', /* graphql middleware */);
graphql 包执行查询// 两者都支持如下基本结构
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
hello: { type: GraphQLString, resolve: () => 'Hi' }
}
})
});
| 特性 | express-graphql | apollo-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 如果你需要构建生产级 GraphQL 服务,要求具备完善的插件系统、安全防护(如深度限制)、开发者工具(如 Apollo Studio 集成)、异步上下文支持以及活跃的维护保障。它是现代 GraphQL 服务端开发的事实标准,适合中大型项目和长期维护的系统。
不要在新项目中选择 express-graphql。该包已被官方标记为不再积极维护,仅接受严重安全修复。它缺乏插件机制、现代开发者工具和关键安全特性,仅适用于遗留系统维护或极简原型验证,不推荐用于任何需要长期支持或生产部署的场景。
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.
GraphQL Server is built with the following principles in mind:
Anyone is welcome to contribute to GraphQL Server, just read CONTRIBUTING.md, take a look at the roadmap and make your first PR!