Migrating from Apollo Server 4


While Apollo Server 4 came with numerous breaking changes, the Apollo Server team is pleased to say that Apollo Server 5 has almost none!

Apollo Server 5 is a small upgrade focused largely on adjusting which versions of Node.js and Express are supported. This page tells you what to do to migrate your server from Apollo Server 4 to Apollo Server 5.

Most users will only have to:

  • Confirm that you are running a non-EOL version of Node.js

  • Maybe do a backwards-compatible upgrade of GraphQL.js

  • If using Express, adjust how you import expressMiddleware

  • If using an HTTP proxy to make HTTP requests, adjust how you configure it

If you are still on Apollo Server v3, the migrating from Apollo Server 3 guide shows you how to migrate directly from v3 to v5.

We recommend that all users of previous versions of Apollo Server upgrade to Apollo Server 5 as soon as possible. Apollo Server 4 is deprecated, with end-of-life on January 26, 2026. Apollo Server 3 has been end-of-life since October 22, 2024. Apollo Server 2 has been end-of-life since October 22, 2023.

Require Node.js v20

Apollo Server 5 supports Node.js v20.0.0 and later. (Apollo Server 4 supports Node.js v14.16.0 and later.) This includes all non-EOL major versions at the time of release.

If you're using Node.js v14, v16, or v18, upgrade your runtime before upgrading to Apollo Server 5 to at least v20; we recommend upgrading all the way to the latest version.

If your server requires you to use an HTTP proxy to make external connections, we strongly recommend running Node.js v24 or newer, which has built-in support for communicating through HTTP proxies.

If you can't upgrade to a non-EOL version of Node.js, you can continue to use Apollo Server 4.

Require GraphQL.js v16.11.0

Apollo Server 5 supports GraphQL.js v16.11.0 and later, as a peer dependency. (Apollo Server 4 supports GraphQL.js v16.6.0 and later.)

Before upgrading to Apollo Server 5, make sure the version of graphql in your server is at least v16.11.0. There are no backward-incompatible changes between v16.6.0 and v16.11.0 (but there are several nice improvements, including a fix to a serious bug that can crash your Node server.)

Separate package for the Express integration

Apollo Server defines a generic API for integrating with web frameworks via separately-distributed integration packages.

Apollo Server 4 includes a built-in integration with version 4 of the Express web framework directly inside the @apollo/server package: the expressMiddleware function exported from @apollo/server/express4.

In Apollo Server 5, integrating with Express works the same way as integrating with any other web framework: you need to install a separate integration package. Apollo maintains an integration with Express v4 (@as-integrations/express4), as well as an integration with Express v5 (@as-integrations/express5). These separate integrations also work with Apollo Server 4, so you may already be using them.

This Apollo Server 4 code:

TypeScript
1import { expressMiddleware } from '@apollo/server/express4';

looks like this in Apollo Server 5:

TypeScript
1import { expressMiddleware } from '@as-integrations/express4';

You must first run npm install @as-integrations/express4.

You can make this change while you're still running Apollo Server 4 before upgrading to Apollo Server 5, or you can make it when you upgrade.

(You may then wish to upgrade Express to v5. If you do, use @as-integrations/express5 instead; it has the same API as @as-integrations/express4.)

Plugins use built-in Node.js fetch to make HTTP requests

Certain features require Apollo Server to make outgoing requests to Apollo GraphOS or another server in your infrastructure. Specifically, the Usage Reporting and Schema Reporting plugins make requests to Apollo GraphOS, and the Subscription Callback plugin makes requests to your GraphOS Router.

In Apollo Server 4, these plugins use the node-fetch npm package by default.

In Apollo Server 5, these plugins use the Node.js built-in fetch implementation by default. (Despite the similar names, these are completely different implementations.)

These implementations both make essentially the same HTTP requests, so this behavior change is largely invisible. However, the two libraries implement HTTP proxy support in different ways. If you had followed our directions to configure your server to use your HTTP proxy using the global-agent npm package, you will need to update your server's configuration.

Note you can override how the plugins make requests by passing a fetcher option to the ApolloServerPluginUsageReporting, ApolloServerPluginSchemaReporting, and ApolloServerPluginSubscriptionCallback functions, though only the usage reporting and schema reporting functions can be configured in Apollo Server 4. The only change in Apollo Server 5 is which fetcher is used by the default, so if you explicitly specify a fetcher to a reporting plugin, nothing will change in Apollo Server 5.

This is simplest if you are running Node.js v24 or newer, where the built-in fetch implementation has support for environment-variable-based proxy configuration.

Specifically, if you have this code in Apollo Server 4:

TypeScript
1import { bootstrap } from 'global-agent';
2bootstrap();

and have set environment variables such as GLOBAL_AGENT_HTTP_PROXY and GLOBAL_AGENT_NO_PROXY, then:

  • Remove the bootstrap import and call.

  • Remove global-agent from your project (eg npm uninstall global-agent).

  • Check what version of Node.js you are running:

    • If you are running Node.js v24 or later, set the NODE_USE_ENV_PROXY environment variable to 1.

    • If you are running Node.js v20 or v22, follow our instructions to set the Undici global dispatcher to the EnvHttpProxyAgent.

  • If you were previously setting the GLOBAL_AGENT_HTTP_PROXY environment variable, set the HTTP_PROXY environment variable to the same value instead.

  • If you were previously setting the GLOBAL_AGENT_HTTPS_PROXY environment variable, set the HTTPS_PROXY environment variable to the same value instead.

  • If you were previously setting the GLOBAL_AGENT_NO_PROXY environment variable, set the NO_PROXY environment variable to the same value instead.

Alternatively, you can continue to use node-fetch with Apollo Server 5. To do this, run npm install node-fetch@2, and pass it as the fetcher option to the plugins that you use:

TypeScript
1import nodeFetchFetcher from 'node-fetch';
2import { ApolloServer } from '@apollo/server';
3import { ApolloServerPluginUsageReporting } from '@apollo/server/plugin/usageReporting';
4import { ApolloServerPluginSchemaReporting } from '@apollo/server/plugin/schemaReporting';
5
6const server = new ApolloServer({
7  typeDefs,
8  resolvers,
9  plugins: [
10    ApolloServerPluginUsageReporting({
11      fetcher: nodeFetchFetcher,
12      // ... other options
13    }),
14    ApolloServerPluginSchemaReporting({
15      fetcher: nodeFetchFetcher,
16      // ... other options
17    }),
18  ],
19});

With this approach, the existing global-agent proxy configuration will continue to work. (v2 is the version of node-fetch used by Apollo Server 4.)

Note: if you have enabled usage reporting by setting APOLLO_KEY and APOLLO_GRAPH_REF without any other configuration, you may not have a call to ApolloServerPluginUsageReporting in your plugins array; you can add a call that just includes fetcher. Similarly, if you set APOLLO_SCHEMA_REPORTING and don't explicitly call ApolloServerPluginSchemaReporting, you can add a call to ApolloServerPluginSchemaReporting that just includes fetcher.

Better default HTTP status for variable coercion errors

Apollo Server 3 responds to requests with variable coercion errors (eg, if a number is passed in the variables map for a variable declared in the operation as a String) with a 400 status code, indicating a client error.

Apollo Server 4 mistakenly responds to these requests with a 200 status code (success) by default. This was an unintended regression. Starting with Apollo Server v4.6.0, users can pass status400ForVariableCoercionErrors: true to restore the intended behavior where variable coercion errors are treated as client errors, and we recommended this value in the documentation. However, for backwards compatibility, Apollo Server 4 still defaulted to responding with a 200 status code to these requests.

Apollo Server 5 restores the intended behavior: status400ForVariableCoercionErrors now defaults to true.

If you have built clients or monitoring that depend on the accidental default behavior here, you can explicitly pass status400ForVariableCoercionErrors: false to the ApolloServer constructor to continue to respond to variable coercion errors with status 200.

We intend to remove this option in future major versions of Apollo Server. If your use case requires the ability to set status400ForVariableCoercionErrors: false, please open an issue to let us know about it.

Standalone server does not use Express

In Apollo Server 4, the simple minimally-configurable server created with startStandaloneServer is implemented using the Express web framework and Apollo Server's built-in expressMiddleware; Express is itself built on top of Node's built-in HTTP server.

In Apollo Server 5, the startStandaloneServer is built directly on top of Node's built-in HTTP server without using Express.

While the TypeScript types in Apollo Server 4 were intended to only allow you to treat the server like an http.Server and not like an Express server, users of startStandaloneServer could have used type-unsafe operations to interact directly with Express-specific features. Additionally, Express does support some extra HTTP-level functionality; for example, by default it sets an x-powered-by: Express response header and a dynamic etag response header based on the hash of the response body.

If you rely on the fact that the startStandaloneServer is powered by Express in Apollo Server 4, you should swap to expressMiddleware to explicitly depend on Express. You can do this before you upgrade from Apollo Server 4 to Apollo Server 5.

Alpha support for incremental delivery

Apollo Server 4 supports the incremental delivery directives @defer and @stream. Enabling these directives requires using v17 of GraphQL.js, which was not yet officially released at the time of AS v4.0.0. The support in Apollo Server 4 was designed specifically around the pre-release v17.0.0-alpha.2 of GraphQL.js, and requires clients to send an accept: multipart/mixed; deferSpec=20220824 header.

At the time of release of Apollo Server v5.0.0, GraphQL.js v17 is still not officially released. Newer alpha releases of GraphQL.js v17 produce a slightly different format of response.

Because GraphQL.js v17's behavior is still in flux, Apollo Server 5 does not yet support the newer incremental delivery format.

Just like in Apollo Server 4, Apollo Server 5 does not support incremental delivery when used with GraphQL.js v16, and does support incremental delivery when used with GraphQL.js v17.0.0-alpha.2 and clients set accept: multipart/mixed; deferSpec=20220824.

The only difference in Apollo Server 5 is that Apollo Server now explicitly checks to see if its version of GraphQL.js is 17.0.0-alpha.2, and only enables it if the version matches exactly. If you are using a newer alpha (or an official v17 release if that has happened by the time you read this), Apollo Server 5 will not support incremental delivery.

We hope to support the newer incremental delivery format in a future version of Apollo Server when GraphQL.js v17 is officially released. Note that because this is an experimental feature that currently requires the use of a pre-release of a dependency, we may drop support for the current format in a minor version release of Apollo Server 5 (but we will at least use the accept header to return reasonable errors to clients).

Unsafe precomputedNonce option to landing page plugins removed

Apollo Server v4.7.3 attempted to fix a challenge with using the landing page plugins on Cloudflare Workers by adding a precomputedNonce option to avoid using crypto functions during server startup and use a fixed value instead of a random value. We quickly learned that this approach was unsafe, as the entire purpose of a nonce is for it to only be used once. The following week, Apollo Server v4.7.4 fixed the Cloudflare Workers compatibility issue in a different way that did not require a precomputed nonce.

For backwards compatibility reasons, we did not remove the precomputedNonce option when we released v4.7.4, though we did make it log a deprecation warning.

In Apollo Server 5, the unsafe precomputedNonce option to the landing page plugins does not exist. We do not expect that many users use this option whose non-deprecated lifetime was 8 days. If you do pass this option to a landing page plugin, you should be able to just delete it.

TypeScript build target changes

Apollo Server 4 is compiled by the TypeScript compiler targeting the ES2020 standard. Apollo Server 5 is compiled targeting the ES2023 standard. This is not expected to cause any issues, because Node.js v20 supports ES2023.

Integration test suite support library TypeScript build target change

The @apollo/server-integration-testsuite used by integration library authors to test their integration no longer uses lib: ["dom"] to tell TypeScript to assume DOM-related symbols are in the global namespace. If your integration library's test suite relied on this behavior, you may need to add lib: ["dom"] to the compilerOptions section of your test suite's tsconfig.json. This should not affect normal use of Apollo Server, just testing for integration library authors.

Feedback

Edit on GitHub

Ask Community