apollo-server vs express-graphql vs graphql-http vs graphql-yoga
Architecting GraphQL Servers: Stability, Standards, and Developer Experience
apollo-serverexpress-graphqlgraphql-httpgraphql-yogaSimilar Packages:

Architecting GraphQL Servers: Stability, Standards, and Developer Experience

apollo-server, express-graphql, graphql-http, and graphql-yoga are all tools used to build GraphQL servers in Node.js, but they serve different roles in the modern ecosystem. apollo-server is a full-featured, batteries-included server solution from the Apollo team, offering built-in schema stitching, federation, and extensive plugins. express-graphql was the standard reference implementation for years but is now deprecated and should not be used for new projects. graphql-http is a lightweight, spec-compliant library that strictly implements the GraphQL over HTTP protocol without extra features, ideal for custom setups. graphql-yoga is a modern, high-performance server built on top of graphql-http and envelop, designed to be framework-agnostic while providing excellent defaults for schema handling, CORS, and file uploads.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
apollo-server013,95426.6 kB883 years agoMIT
express-graphql06,255-546 years agoMIT
graphql-http0363412 kB10a month agoMIT
graphql-yoga08,529309 kB1485 days agoMIT

Architecting GraphQL Servers: Stability, Standards, and Developer Experience

Building a GraphQL server in Node.js used to mean reaching for express-graphql or apollo-server. Today, the landscape has shifted significantly. We now have lightweight, spec-compliant options like graphql-http and modern, developer-friendly servers like graphql-yoga. Meanwhile, express-graphql has reached the end of its life. Let's break down how these tools differ in real-world scenarios.

πŸ›‘ Maintenance Status: Active vs. Deprecated

The first thing you need to know is which tools are safe to use. express-graphql is deprecated. The maintainers have officially stopped updating it, and it does not support the latest GraphQL specification features or security patches.

// express-graphql: DEPRECATED - Do not use in new projects
// npm install express-graphql (NOT RECOMMENDED)
const { graphqlHTTP } = require('express-graphql');
app.use('/graphql', graphqlHTTP({ schema }));

apollo-server is actively maintained but is currently in a transition phase. The team is encouraging users to move toward more modular approaches, though the main package remains stable for enterprise use.

// apollo-server: Active but heavy
// npm install apollo-server
const { ApolloServer } = require('apollo-server');
const server = new ApolloServer({ typeDefs, resolvers });

graphql-yoga and graphql-http are the modern standards. graphql-yoga is actively developed by the Envelop team and is rapidly becoming the default choice for new applications due to its balance of features and performance.

// graphql-yoga: Modern, active, recommended
// npm install graphql-yoga
import { createYoga } from 'graphql-yoga';
const yoga = createYoga({ schema });

graphql-http is a low-level library that powers many modern servers. It is stable, strictly follows the spec, and is perfect if you want to build your own server logic.

// graphql-http: Minimal, spec-compliant
// npm install graphql-http
import { createHandler } from 'graphql-http/lib/use/http';
app.all('/graphql', createHandler({ schema }));

βš™οΈ Setup Complexity: Batteries-Included vs. DIY

How much code do you need to write to get a server running? This varies wildly between these packages.

apollo-server tries to do everything for you. It includes built-in CORS, body parsing, and even a playground UI. You just pass your schema and resolvers.

// apollo-server: High-level setup
const server = new ApolloServer({
  typeDefs,
  resolvers,
  introspection: true, // Enabled by default in dev
});
await server.start();
server.applyMiddleware({ app });

graphql-yoga offers a similar "batteries-included" experience but is lighter. It automatically handles CORS, content-type negotiation, and provides a playground without extra config.

// graphql-yoga: Simple, modern setup
import { createYoga } from 'graphql-yoga';
import { createServer } from 'http';

const yoga = createYoga({ schema });
const server = createServer(yoga);
server.listen(4000);

graphql-http gives you almost nothing out of the box. It is just a handler function. You must set up your own HTTP server, handle CORS, and manage error responses manually.

// graphql-http: Low-level, manual setup
import { createHandler } from 'graphql-http/lib/use/http';
import express from 'express';
import cors from 'cors';

const app = express();
app.use(cors()); // You must add this yourself
app.all('/graphql', createHandler({ schema }));

express-graphql (again, deprecated) was somewhere in the middle. It handled the basics but required manual middleware stacking for things like advanced CORS or compression.

// express-graphql: Legacy mid-level setup
app.use('/graphql', graphqlHTTP({
  schema,
  graphiql: true, // Had to enable UI manually
  customFormatErrorFn: (err) => ({ message: err.message })
}));

πŸ“‘ Protocol Support: Subscriptions and Uploads

Modern apps need more than just queries and mutations. They often need real-time updates (subscriptions) and file uploads. Support for these varies greatly.

graphql-yoga has excellent support for both subscriptions and file uploads built-in. It uses standard WebSocket protocols and multipart forms without needing extra plugins.

// graphql-yoga: Built-in subscriptions & uploads
import { createYoga } from 'graphql-yoga';
import { useGraphQLUpload } from 'graphql-upload'; // Often integrated or easy to add

const yoga = createYoga({
  schema,
  // Subscriptions work out of the box with standard clients
});

apollo-server supports subscriptions, but historically required a separate apollo-server-express setup with specific WebSocket configuration. File uploads also require adding the graphql-upload package and configuring it explicitly.

// apollo-server: Requires extra config for uploads/subs
import { ApolloServer } from 'apollo-server-express';
import { GraphQLUpload } from 'graphql-upload';

const server = new ApolloServer({
  typeDefs,
  resolvers: { Upload: GraphQLUpload },
  // Needs specific WebSocket server setup for subscriptions
});

graphql-http strictly implements the HTTP spec. It does not support subscriptions or multipart uploads natively because those are not part of the core GraphQL-over-HTTP spec. You must implement these yourself or layer other libraries on top.

// graphql-http: No native subscription/upload support
// You must write custom logic for WebSocket or multipart handling
app.all('/graphql', createHandler({ schema }));
// Subscriptions require a separate WebSocket handler

express-graphql never had native support for subscriptions or uploads. Developers always had to hack together third-party middleware, which was often brittle and hard to maintain.

// express-graphql: No native support
// Required complex, unofficial middleware for uploads
// Impossible to do subscriptions cleanly

🧩 Extensibility: Plugins vs. Middleware

How do you add logging, authentication, or caching? The approach differs between "plugin ecosystems" and "standard middleware."

apollo-server uses a plugin system. You can tap into lifecycle events like requestDidStart to add custom logic. This is powerful but ties you to Apollo's specific API.

// apollo-server: Plugin system
const server = new ApolloServer({
  schema,
  plugins: [
    {
      requestDidStart() {
        return {
          didResolveOperation({ request, document }) {
            console.log('Query:', request.operationName);
          }
        };
      }
    }
  ]
});

graphql-yoga is built on envelop, a powerful plugin system that is framework-agnostic. You can use hundreds of existing plugins for auth, caching, or tracing just by adding them to the use array.

// graphql-yoga: Envelop plugins
import { useLogger, useAuth } from 'graphql-yoga'; // Example plugins

const yoga = createYoga({
  schema,
  plugins: [
    useLogger(),
    // useAuth({ ... }) 
  ]
});

graphql-http relies entirely on standard Express (or other framework) middleware. There is no plugin system. If you want logging, you use morgan. If you want auth, you use passport. This is simple but less integrated with the GraphQL execution context.

// graphql-http: Standard middleware
app.use(morgan('combined')); // Logging
app.use(authMiddleware); // Auth
app.all('/graphql', createHandler({ schema }));

express-graphql also relied on standard middleware. It offered a customRootValue or context function, but lacked a structured plugin lifecycle, making complex cross-cutting concerns harder to implement cleanly.

// express-graphql: Basic context injection
app.use('/graphql', graphqlHTTP((req) => ({
  schema,
  context: { user: req.user }, // Manual context passing
  customFormatErrorFn: (err) => logError(err)
})));

🌐 Framework Agnosticism: Node vs. Edge

Where can you run your server? This is becoming a key decision factor as edge computing grows.

graphql-yoga is designed to be framework-agnostic. It works on Node.js, Cloudflare Workers, Vercel Edge Functions, and Deno. It adapts to the environment automatically.

// graphql-yoga: Runs anywhere (Node, Edge, Deno)
import { createYoga } from 'graphql-yoga';
// Works in Cloudflare Workers without changes
export default { fetch: createYoga({ schema }).fetch };

graphql-http is also highly portable. Since it only depends on standard web APIs (Request/Response), it runs easily on any JavaScript runtime that supports these standards.

// graphql-http: Portable via Web Standards
import { createHandler } from 'graphql-http/lib/use/fetch';
// Adapts to any fetch-compatible environment
const handler = createHandler({ schema });

apollo-server is primarily tied to Node.js. While there are some community efforts to adapt it, it is not natively designed for edge runtimes or serverless environments that differ significantly from the Node HTTP module.

// apollo-server: Node.js focused
// Requires Node-specific HTTP modules
const server = new ApolloServer({ schema });
// Hard to deploy to non-Node edge environments

express-graphql is strictly tied to Express and Node.js. It cannot run on edge runtimes or other frameworks without a complete rewrite of the underlying logic.

// express-graphql: Node/Express only
// Cannot run on Cloudflare Workers or Deno

πŸ“Š Summary: Key Differences

Featureapollo-serverexpress-graphqlgraphql-httpgraphql-yoga
Statusβœ… Active (Heavy)❌ Deprecatedβœ… Active (Minimal)βœ… Active (Modern)
SetupBatteries-includedMid-levelDIY / MinimalBatteries-included
SubscriptionsSupported (Config heavy)❌ No❌ No (Spec only)βœ… Built-in
File UploadsSupported (Plugin needed)❌ No❌ Noβœ… Built-in
ExtensibilityApollo PluginsMiddlewareMiddlewareEnvelop Plugins
RuntimeNode.jsNode.jsAny (Web Standards)Any (Web Standards)

πŸ’‘ The Big Picture

express-graphql is dead. If you see it in a codebase, plan to migrate away from it immediately. It lacks security updates and modern features.

apollo-server is still a valid choice for large enterprises already invested in the Apollo ecosystem, especially if you need Federation or Studio integration. However, its complexity and Node-only focus make it less ideal for new, agile projects.

graphql-http is the perfect tool for library authors or developers who want to build a custom server from the ground up with zero opinions. It is the "assembly language" of GraphQL servers.

graphql-yoga is the sweet spot for most teams today. It combines the ease of use of apollo-server with the performance and standards compliance of graphql-http. It works everywhere (Node, Edge, Serverless) and includes modern features like subscriptions and uploads without extra hassle. For 90% of new projects, this is the right choice.

How to Choose: apollo-server vs express-graphql vs graphql-http vs graphql-yoga

  • apollo-server:

    Choose apollo-server if you need a robust, enterprise-grade solution with built-in support for Apollo Federation, schema stitching, and a rich plugin ecosystem. It is ideal for large-scale microservices architectures where you need tight integration with Apollo Studio for monitoring and governance. However, be aware that it is heavier and less flexible than newer alternatives if you only need a simple API.

  • express-graphql:

    Do NOT choose express-graphql for any new project. This package is officially deprecated and no longer maintained. While you might see it in legacy codebases, migrating away from it to a supported alternative like graphql-yoga or apollo-server is critical for security and compatibility with modern GraphQL standards.

  • graphql-http:

    Choose graphql-http if you want a minimal, strictly spec-compliant implementation and plan to build your own server logic from scratch. It is perfect for developers who need total control over the HTTP layer, custom middleware, or integration into non-Express environments (like Fastify or raw Node http) without opinionated defaults getting in the way.

  • graphql-yoga:

    Choose graphql-yoga for most new projects requiring a balance of performance, ease of use, and modern standards. It provides a great developer experience with built-in support for CORS, file uploads, and GraphQL subscriptions out of the box, while remaining lightweight and framework-agnostic. It is the recommended path for teams wanting a 'batteries-included' feel without the heavy footprint of older monolithic servers.

README for apollo-server

Apollo Server

A TypeScript GraphQL Server for Express, Koa, Hapi, Lambda, and more.

npm version Build Status Join the community forum Read CHANGELOG

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.

Principles

Apollo Server is built with the following principles in mind:

  • By the community, for the community: Its development is driven by the needs of developers.
  • Simplicity: By keeping things simple, it is more secure and easier to implement and contribute.
  • Performance: It is well-tested and production-ready.

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

Getting started

To get started with Apollo Server:

  • Install with npm install apollo-server-<integration> graphql
  • Write a GraphQL schema
  • Use one of the following snippets

There are two ways to install Apollo Server:

  • Standalone: For applications that do not require an existing web framework, use the apollo-server package.
  • Integrations: For applications with a web framework (e.g. express, koa, hapi, etc.), use the appropriate Apollo Server integration package.

For more info, please refer to the Apollo Server docs.

Installation: Standalone

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 GraphQLSchema object from graphql-js can also be specified instead of typeDefs and resolvers using the schema property:

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.

Installation: Integrations

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:

Context

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
    };
  },
})

Documentation

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.

Development

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
  • Build and test
npm install
npm test
  • To run individual test files, run 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.

Community

Are you stuck? Want to contribute? Come visit us in the Apollo community forum!

Maintainers

Who is Apollo?

Apollo builds open-source software and a graph platform to unify GraphQL across your apps and services. We help you ship faster with:

  • Apollo Studio – A free, end-to-end platform for managing your GraphQL lifecycle. Track your GraphQL schemas in a hosted registry to create a source of truth for everything in your graph. Studio provides an IDE (Apollo Explorer) so you can explore data, collaborate on queries, observe usage, and safely make schema changes.
  • Apollo Federation – The industry-standard open architecture for building a distributed graph. Use Apollo’s gateway to compose a unified graph from multiple subgraphs, determine a query plan, and route requests across your services.
  • Apollo Client – The most popular GraphQL client for the web. Apollo also builds and maintains Apollo iOS and Apollo Android.
  • Apollo Server – A production-ready JavaScript GraphQL server that connects to any microservice, API, or database. Compatible with all popular JavaScript frameworks and deployable in serverless environments.

Learn how to build with Apollo

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.