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 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.
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.

Apollo Server is a community-maintained open-source GraphQL server. It works with many Node.js HTTP server frameworks, or can run on its own with a built-in Express server. Apollo Server works with any GraphQL schema built with GraphQL.js--or define a schema's type definitions using schema definition language (SDL).
Read the documentation for information on getting started and many other use cases and follow the CHANGELOG for updates.
Apollo Server is built with the following principles in mind:
Anyone is welcome to contribute to Apollo Server, just read CONTRIBUTING.md, take a look at the roadmap and make your first PR!
To get started with Apollo Server:
npm install apollo-server-<integration> graphqlThere are two ways to install Apollo Server:
apollo-server package.express, koa, hapi, etc.), use the appropriate Apollo Server integration package.For more info, please refer to the Apollo Server docs.
In a new project, install the apollo-server and graphql dependencies using:
npm install apollo-server graphql
Then, create an index.js which defines the schema and its functionality (i.e. resolvers):
const { ApolloServer, gql } = require('apollo-server');
// The GraphQL schema
const typeDefs = gql`
type Query {
"A simple type for getting started!"
hello: String
}
`;
// A map of functions which return data for the schema.
const resolvers = {
Query: {
hello: () => 'world',
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
Due to its human-readability, we recommend using schema-definition language (SDL) to define a GraphQL schema--a
GraphQLSchemaobject fromgraphql-jscan also be specified instead oftypeDefsandresolversusing theschemaproperty:const server = new ApolloServer({ schema: ... });
Finally, start the server using node index.js and go to the URL returned on the console.
For more details, check out the Apollo Server Getting Started guide and the fullstack tutorial.
For questions, the Apollo community forum is a great place to get help.
While the standalone installation above can be used without making a decision about which web framework to use, the Apollo Server integration packages are paired with specific web frameworks (e.g. Express, Koa, hapi).
The following web frameworks have Apollo Server integrations, and each of these linked integrations has its own installation instructions and examples on its package README.md:
A request context is available for each request. When context is defined as a function, it will be called on each request and will receive an object containing a req property, which represents the request itself.
By returning an object from the context function, it will be available as the third positional parameter of the resolvers:
new ApolloServer({
typeDefs,
resolvers: {
Query: {
books: (parent, args, context, info) => {
console.log(context.myProperty); // Will be `true`!
return books;
},
}
},
context: async ({ req }) => {
return {
myProperty: true
};
},
})
The Apollo Server documentation contains additional details on how to get started with GraphQL and Apollo Server.
The raw Markdown source of the documentation is available within the docs/ directory of this monorepo--to contribute, please use the Edit on GitHub buttons at the bottom of each page.
If you wish to develop or contribute to Apollo Server, we suggest the following:
Fork this repository
Install Direnv (a tool that automatically sets up environment variables in project directories) or nvm. We use nvm to ensure we're running the expected version of Node (and we use Direnv to install and run nvm automatically).
Install the Apollo Server project on your computer
git clone https://github.com/[your-user]/apollo-server
cd apollo-server
direnv allow # sets up nvm for you; if you installed nvm yourself, try `nvm install` instead
npm install
npm test
npm run pretest && npx jest packages/apollo-server-foo/src/__tests__/bar.test.ts. Note that you do need to re-compile TypeScript before each time you run a test, or changes across packages may not be picked up. Instead of running npm run pretest from scratch before each test run, you can also run tsc --build tsconfig.json --watch in another shell, or use the VSCode Run Build Task to run that for you.Are you stuck? Want to contribute? Come visit us in the Apollo community forum!
Apollo builds open-source software and a graph platform to unify GraphQL across your apps and services. We help you ship faster with:
Check out the Odyssey learning platform, the perfect place to start your GraphQL journey with videos and interactive code challenges. Join the Apollo Community to interact with and get technical help from the GraphQL community.