apollo-server, express-graphql, and graphql-tools are foundational packages in the Node.js GraphQL ecosystem, each serving distinct but sometimes overlapping roles. apollo-server is a standalone GraphQL server implementation that includes built-in support for Express and other web frameworks, offering features like schema stitching, subscriptions, and developer tooling. express-graphql is a minimal middleware that adds GraphQL execution to an existing Express application, relying on the core graphql package for query resolution. graphql-tools provides utilities for constructing and manipulating GraphQL schemas programmatically, notably through the makeExecutableSchema function, and is often used alongside server implementations to define resolvers and type definitions cleanly.
When setting up a GraphQL API in Node.js, developers often encounter three key packages: apollo-server, express-graphql, and graphql-tools. While they all relate to GraphQL, they serve different purposes—one is a full server, one is a lightweight middleware, and one is a schema utility library. Understanding their roles prevents architectural missteps, especially since one of them is no longer maintained.
First, a critical update: express-graphql is officially deprecated. The npm page states: "This package is no longer maintained. We recommend using Apollo Server instead." This means you should not use express-graphql in new projects. We include it here only for context and migration guidance.
In contrast, both apollo-server and graphql-tools are actively maintained and widely used in production systems.
apollo-server: Full-Featured GraphQL Serverapollo-server is a complete GraphQL server implementation. It handles HTTP requests, parses queries, executes resolvers, formats errors, and serves GraphQL Playground (in development). It can run standalone or integrate with Express, Koa, Hapi, and more.
// apollo-server: Standalone server
import { ApolloServer } from 'apollo-server';
import { typeDefs, resolvers } from './schema.js';
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => console.log(`Server ready at ${url}`));
express-graphql: Minimal Express Middleware (Deprecated)express-graphql is just a middleware that plugs GraphQL execution into an Express app. It delegates almost everything to the graphql package and offers minimal configuration.
// express-graphql: Deprecated usage
import express from 'express';
import { graphqlHTTP } from 'express-graphql';
import { schema } from './schema.js';
const app = express();
app.use('/graphql', graphqlHTTP({ schema, graphiql: true }));
app.listen(4000);
🔴 Do not use this in new code. Migrate to Apollo Server if you're maintaining an old codebase.
graphql-tools: Schema Construction Utilitiesgraphql-tools doesn’t run a server—it helps you build schemas. Its flagship function, makeExecutableSchema, combines type definitions (SDL) and resolver objects into a single executable schema object that any GraphQL server can use.
// graphql-tools: Schema creation
import { makeExecutableSchema } from 'graphql-tools';
const typeDefs = `
type Query {
hello: String
}
`;
const resolvers = {
Query: { hello: () => 'Hello world!' }
};
const schema = makeExecutableSchema({ typeDefs, resolvers });
// Now pass `schema` to Apollo Server, Yoga, or any other server
graphql-tools with apollo-serverMost professional Apollo Server setups use graphql-tools under the hood (even if indirectly). Here’s how they work together:
// Combined: graphql-tools + apollo-server
import { ApolloServer } from 'apollo-server';
import { makeExecutableSchema } from 'graphql-tools';
const schema = makeExecutableSchema({
typeDefs: `type Query { user(id: ID!): User } type User { id: ID! name: String }`,
resolvers: {
Query: { user: (_, { id }) => ({ id, name: 'Alice' }) }
}
});
const server = new ApolloServer({ schema });
server.listen();
Note: Modern Apollo Server versions accept typeDefs and resolvers directly and internally use graphql-tools, so explicit makeExecutableSchema isn’t always needed—but it’s still useful for advanced cases like schema merging.
express-graphql Today?Beyond deprecation, express-graphql lacks:
Apollo Server provides all these out of the box.
graphql-toolsWhere graphql-tools shines is in complex schema management:
Before Apollo Federation, graphql-tools enabled combining multiple GraphQL services into one schema:
import { stitchSchemas } from 'graphql-tools';
const gatewaySchema = stitchSchemas({
subschemas: [
{ schema: userServiceSchema },
{ schema: orderServiceSchema }
]
});
Note: For new microservices, use Apollo Federation instead—but
graphql-toolsstill powers many stitching-based systems.
Generate realistic mock data from your schema:
import { addMocksToSchema } from 'graphql-tools';
const mockedSchema = addMocksToSchema({ schema });
// Now all resolvers return auto-generated mock data
This is invaluable for frontend teams working before backend APIs are ready.
Provides structured error formatting, masking stack traces in production, and extension points:
const server = new ApolloServer({
formatError: (err) => ({
message: err.message,
code: err.extensions?.code || 'INTERNAL_ERROR'
})
});
Offers basic error handling but no customization hooks beyond a global formatError option.
Not applicable—it doesn’t handle runtime execution, so no error formatting.
| Scenario | Recommended Package(s) |
|---|---|
| New GraphQL API from scratch | apollo-server (with or without explicit graphql-tools) |
| Need modular schema design or mocking | graphql-tools + any server (e.g., Apollo Server) |
| Maintaining old Express app with GraphQL | Migrate from express-graphql to apollo-server-express |
| Building a GraphQL gateway | Use @apollo/server with Federation, not graphql-tools stitching |
apollo-server is your go-to for production GraphQL servers—feature-rich, secure, and well-supported.express-graphql is deprecated. Avoid it in new projects; plan to migrate existing uses.graphql-tools is not a server—it’s a toolkit for schema authoring, testing, and composition. It complements server libraries rather than replacing them.In modern architectures, you’ll often see apollo-server and graphql-tools used together: the former runs the API, the latter builds the schema. Understanding this division of labor leads to cleaner, more maintainable GraphQL implementations.
Choose graphql-tools when you need flexible, programmatic schema construction—especially for modular schema design, schema merging, or mock generation—without tying yourself to a specific server implementation. It pairs well with any GraphQL server (including Apollo Server) and is essential for advanced schema workflows like federation or testing.
Choose apollo-server if you need a production-ready, batteries-included GraphQL server with integrated tooling like Apollo Studio, persisted queries, and built-in support for subscriptions and file uploads. It’s ideal for teams building full-featured GraphQL APIs who want conventions, security defaults, and extensibility without assembling low-level pieces manually.
Choose express-graphql only if you’re maintaining a legacy Express app that already uses this middleware and you need minimal GraphQL integration without additional dependencies. Note that it is officially deprecated and should not be used in new projects — consider migrating to Apollo Server or another modern alternative.
ERROR: No README data found!