Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions website/pages/docs/_meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const meta = {
},
'getting-started': '',
'running-an-express-graphql-server': '',
'migrating-from-express-graphql': '',
'graphql-clients': '',
'basic-types': '',
'passing-arguments': '',
Expand Down
215 changes: 215 additions & 0 deletions website/pages/docs/migrating-from-express-graphql.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
---
title: Migrate from Express GraphQL to GraphQL over HTTP
sidebarTitle: Migrate from Express GraphQL
---

# Migrate from Express GraphQL to GraphQL over HTTP

When GraphQL was open-sourced in 2015, `express-graphql` quickly became the standard way to run a GraphQL server in Node.js.
Built as middleware for Express, it offered a simple and reliable development experience. However, it hasn’t received a
feature update since 2018 and is no longer actively maintained. For modern applications, it lacks support for new transport
features, fine-grained request handling, and deployment flexibility.

[`graphql-http`](https://github.com/graphql/graphql-http) is a lightweight implementation of
the [GraphQL over HTTP specification](https://graphql.github.io/graphql-over-http/draft/). 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` is no longer supported

The library has not received updates for some time. As a deprecated package, 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.

### Understand current limitations

Although `graphql-http` is a strong foundation for modern GraphQL servers, it's important to note what it doesn't include:

- It doesn't support subscriptions or experimental features like incremental delivery (`@defer` / `@stream`) out of the box.
- These limitations are by design. `graphql-http` strictly adheres to the current
[GraphQL over HTTP specification](https://graphql.github.io/graphql-over-http/draft/), which does
not yet define behavior for those features.
- If your application needs support for subscriptions or live queries, consider using complementary libraries like
[`graphql-ws`](https://github.com/enisdenjo/graphql-ws) or [`graphql-sse`](https://github.com/enisdenjo/graphql-sse).

These are not limitations unique to `graphql-http`. `express-graphql` does not support these features either, but it's important
to set the right expectations about extensibility.

## 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:

```bash
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:

```js
// 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:

```js
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 both `GET` and `POST` 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`:

```js
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 tool like [Ruru](https://www.npmjs.com/package/ruru) to serve an interactive GraphQL UI locally:

```bash
npx ruru -SP -p 4001 -e http://localhost:4000/graphql
```

- Or serve a static HTML page that embeds [GraphiQL](https://github.com/graphql/graphiql) 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 experimenting with streaming features like `@defer`, test that your client can handle multipart/mixed responses.
Support for this content type is still limited in many tools.

## 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 a `200 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 development tool like Ruru to serve 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:

```js
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` aims to support the GraphQL over HTTP spec, including eventually supporting response streaming. However,
support for features like `@defer` and `@stream` is still evolving. These capabilities are experimental in `graphql-js`
and not yet supported by `graphql-http`.

- Most GraphQL clients don’t yet support multipart responses.
- Server-side support for streaming may be incomplete or inconsistent.
- If your app relies on incremental delivery, consider using a transport library like
[`graphql-sse`](https://github.com/enisdenjo/graphql-sse) instead.

## What's next

For advanced customization, consider integrating `graphql-http` with [Envelop](https://the-guild.dev/graphql/envelop), a
plugin-based GraphQL execution layer.

If you’re looking for a batteries-included solution that includes transport, subscriptions, and a GraphQL IDE,
[GraphQL Yoga](https://the-guild.dev/graphql/yoga-server) might be a better fit. It builds on Envelop and provides its
own transport layer, separate from `graphql-http`.

For a list of other spec-compliant server implementations, see the [`graphql-http` server list](https://github.com/graphql/graphql-http?tab=readme-ov-file#servers).
Loading