Migrate from Express GraphQL to GraphQL over HTTP
When GraphQL was open-sourced in 2015, express-graphql
quickly became the
default way to run a GraphQL server in JavaScript. Built as middleware for Express,
it provided a simple and reliable way to serve GraphQL queries using Node.js.
While express-graphql
still receives occasional updates for security and spec alignment, it is no longer actively developed. This limits its suitability for projects that need more control over request handling, streaming, or support for modern deployment environments.
graphql-http
is a lightweight implementation of the GraphQL over HTTP specification. It’s framework-agnostic, built to be composable, and easy to integrate into different server environments. Unlike express-graphql
, it can run in a wide range of environments, not just Express.
This guide is for developers currently using express-graphql
who want to
modernize their stack, adopt the HTTP spec, or decouple their GraphQL server
from Express.
Benefits of migrating
express-graphql
has limited future support
The library is no longer under active development. While it is still maintained for security and spec compliance, it is not evolving with the GraphQL ecosystem. This makes it less flexible for long-term projects.
graphql-http
is spec-compliant by default
The GraphQL over HTTP specification defines how GraphQL should be transported over HTTP, including request methods, status codes, content types, and more. graphql-http
follows this spec precisely, helping your server behave predictably and remain compatible with future tooling.
It’s framework-agnostic by design
Instead of relying on Express, graphql-http
is built on the standard Web Request
and Response
interfaces. It works with Express, Fastify, Node’s native HTTP server, and can also be used in serverless and edge environments.
It fits into modern JavaScript stacks
graphql-http
supports ESM and works well with modern build tools and lightweight deployment platforms. Its composable design makes it easy to customize, wrap, and integrate into different application architectures.
Designed for future compatibility
As GraphQL evolves, tools and platforms increasingly expect spec-compliant behavior. Migration to graphql-http
helps ensure your
server will support future capabilities without relying on workarounds.
Migration guide
The following steps walk through how to migrate an existing express-graphql
server to use graphql-http
. The steps assume you already have a working Express app using express-graphql
.
Prerequisites
Before you begin, make sure you have:
- Node.js 16 or later
- A GraphQL schema
- An existing Express app configured with
express-graphql
Step 1: Install graphql-http and the Express adapter
Install the core graphql-http
package along with its Express adapter:
npm install graphql graphql-http @graphql-http/express
The graphql
package is a peer dependency of graphql-http
, and must be installed if it isn’t already.
Step 2: Remove express-graphql middleware
In your Express server file, remove the express-graphql
middleware:
// Before (using express-graphql)
import { graphqlHTTP } from 'express-graphql';
app.use('/graphql', graphqlHTTP({
schema,
graphiql: true,
}));
Step 3: Add graphql-http middleware with createHandler
Replace it with the graphql-http
handler using the Express adapter:
import express from 'express';
import { createHandler } from 'graphql-http/lib/use/express';
import { schema } from './schema.js';
const app = express();
app.all('/graphql', createHandler({ schema }));
app.listen(4000);
- Use
app.all()
to allow bothGET
andPOST
requests. - The handler accepts an options object similar to
express-graphql
.
Step 4: Handle context, error formatting, and extensions
You can provide options like context
, rootValue
, and formatError
:
app.all('/graphql', createHandler({
schema,
context: async (req, res) => {
return { user: await authenticate(req) };
},
formatError: (error) => ({
message: error.message,
path: error.path,
}),
}));
context
can be a static object or an async function.- You can also pass
rootValue
, or extend responses with custom logic.
Step 5: Add a GraphQL IDE (optional)
Unlike express-graphql
, graphql-http
does not include a built-in GraphQL IDE. If you want to add one:
- Use a plugin that serves an interactive GraphQL UI alongside your endpoint
- Or serve a static HTML page that loads your preferred IDE from a CDN
In either case, make sure to restrict access in production environments.
Step 6: Test your setup
After migrating, verify that your server responds correctly:
- Send queries and mutations using your preferred client.
- Check for proper HTTP status codes and response shapes.
- If you’re using streaming features like
@defer
, ensure your client can handlemultipart/mixed
responses.
Best practices
When migrating from express-graphql
to graphql-http
, there are a few key differences and potential pitfalls to keep in mind. These tips can help you avoid common issues and ensure a smoother transition.
Be aware of different error behavior
graphql-http
follows the GraphQL over HTTP spec closely, which means error formatting and status codes may differ from what you’re used to with express-graphql
. For example:
- Invalid queries may return a
400 Bad Request
instead of a200 OK
. - Errors in parsing or validation are surfaced earlier and more strictly.
- You can customize error output using the
formatError
option, but it must conform to the spec.
This can affect client expectations if they were relying on express-graphql
’s more lenient defaults.
Consider IDE support in development
express-graphql
includes GraphiQL by default in development mode. graphql-http
does not.
To restore this functionality, consider:
- Use a middleware plugin that serves an in-browser IDE.
- Host a static IDE page at a separate endpoint.
- Ensure the IDE is only available in non-production environments.
Watch for framework-specific middleware behavior
Since graphql-http
is framework-agnostic, it does not handle things like body parsing, CORS, or compression. You’ll need to ensure those are handled appropriately by your Express setup:
import cors from 'cors';
import express from 'express';
app.use(cors());
app.use(express.json());
This gives you more control but requires a bit more setup.
Understand streaming and file upload limitations
graphql-http
supports streaming via multipart/mixed
, which enables features like @defer
. However:
- Many clients don’t yet support this content type.
- File uploads are not built-in, and must be handled outside the core handler.
Test your client integration carefully if you rely on these features.
What’s next
Once you’re up and running with graphql-http
, you can start extending your server with validation rules, monitoring, custom error handling, or alternative transports. For more advanced use cases, consider combining graphql-http
with tools like envelop
or graphql-yoga
.
For details on the GraphQL over HTTP specification, see the official spec draft.