Join us from October 8-10 in New York City to learn the latest tips, trends, and news about GraphQL Federation and API platform engineering.Join us for GraphQL Summit 2024 in NYC
Docs
Start for Free

Migrating to Apollo Server 4


📣 Apollo Server 4 is generally available!

4 focuses on improving Apollo Server's extensibility and making it simpler to use, maintain, and . To learn more about the inspiration behind this release, see the Apollo Server Roadmap.

Apollo Server 4 provides the following features:

  • A well-defined API with a stable HTTP abstraction, enabling contributors to easily build and maintain integrations in their preferred frameworks.
  • A new @apollo/server package, combining numerous smaller packages and including the startStandaloneServer and expressMiddleware functions.
  • Packages that can be used as either ECMAScript or CJS modules.
  • Experimental support for incremental delivery when combined with a pre-release of graphql-js.

For a list of all breaking changes, see the changelog.

🚚 This guide helps you migrate from Apollo Server 3 to Apollo Server 4. If you are using Apollo Server 2, you must first migrate to Apollo Server 3 before following this guide.

We recommend that all users of previous versions of Apollo Server upgrade to Apollo Server 4 as soon as possible. Apollo Server 3 is deprecated with an end-of-life date of October 22, 2024. Apollo Server 2 is end-of-life as of October 22, 2023.

The new @apollo/server package

Apollo Server 3 is distributed as a fixed set of packages for integrating with different web frameworks and environments. The main "batteries-included" apollo-server package reduces setup time by providing a minimally customizable .

In Apollo Server 3, the apollo-server-core package defines an ApolloServer "base" class, which each integration package (apollo-server-express,apollo-server-lambda, etc.) subclasses with a slightly different API. This packaging structure means that new integration package releases are lockstep versioned to Apollo Server itself, making it challenging to support major versions of frameworks and add integration-specific changes. Additionally, Apollo Server 3 doesn't provide a way to add new integrations for additional frameworks.

Apollo Server 4 takes a different approach to integrations by providing a stable web framework integration API, which includes explicit support for framework life cycles.

The new @apollo/server package contains:

  • The ApolloServer class
  • An Express 4 integration (similar to Apollo Server 3's apollo-server-express package)
  • A standalone server (similar to Apollo Server 3's apollo-server package)
  • A set of core plugins (similar to Apollo Server 3's apollo-server-core package)

There are no integration-specific subclasses in Apollo Server 4. Instead, there's a single ApolloServer class with a single API that all integrations use.

In Apollo Server 3, the Apollo Server core team was responsible for maintaining every integration package. With Apollo Server 4, the AS core team no longer directly maintains most integration packages. Instead, we work with the broader open source community to maintain Apollo Server integrations, enabling those who regularly use different web frameworks to make the best choices for their framework's integration.

For those migrating from Apollo Server 3 to Apollo Server 4, use the below flowchart to see your migration path:

N
Yes
N
Yes
Am I using the apollo-server package?
Am I using the apollo-server-express package?
Use the startStandaloneServer function
See if a community-supported integration exists
Use the expressMiddleware function

  • If you're currently using the apollo-server package, you should use the startStandaloneServer function.
  • If you're currently using the apollo-server-express package, you should use the expressMiddleware function.

The @apollo/server package exports these functions alongside the ApolloServer class.

If you are using another Apollo Server 3 framework integration package (such as apollo-server-koa or apollo-server-lambda), check out our list of integrations to see if a community-maintained integration package exists for your framework of choice.

If there is no Apollo Server integration for your favorite framework yet, help the broader community by building a new integration! You can also join the discussions about maintaining our existing integrations.

Below are a few high-level changes for using framework integrations:

  • You can pass your context initialization function directly to your framework's integration function (e.g., expressMiddleware or startStandaloneServer) instead of the ApolloServer constructor.
  • You are responsible for setting up HTTP body parsing and CORS using your framework integration's standard functionality.
  • If you want your server to listen on a specific URL path, pass that path directly to your framework's instead of using the path option. If you did not specify a URL path, the default in Apollo Server 3 was /graphql, so to preserve existing behavior, you should specify that path explicitly.

The following sections show how servers using apollo-server or apollo-server-express can update to Apollo Server 4.

NOTE

In the examples below, we use top-level await calls to start our server asynchronously. If you'd like to see how we set this up, check out the Getting Started guide for details.

Migrate from apollo-server

In Apollo Server 3, the apollo-server package is a "batteries-included" package that wraps apollo-server-express, providing an HTTP server with minimal HTTP-level customization.

If you used the "batteries included" apollo-server package in Apollo Server 3, use the startStandaloneServer function in Apollo Server 4.

This Apollo Server 3 code:

apollo-server-3-standalone.ts
// npm install apollo-server graphql
import { ApolloServer } from 'apollo-server';
import { typeDefs, resolvers } from './schema';
interface MyContext {
token?: String;
}
const server = new ApolloServer({
typeDefs,
resolvers,
context: async ({ req }) => ({ token: req.headers.token }),
});
const { url } = await server.listen(4000);
console.log(`🚀 Server ready at ${url}`);
apollo-server-3-standalone.js
// npm install apollo-server graphql
import { ApolloServer } from 'apollo-server';
import { typeDefs, resolvers } from './schema';
const server = new ApolloServer({
typeDefs,
resolvers,
context: async ({ req }) => ({ token: req.headers.token }),
});
const { url } = await server.listen(4000);
console.log(`🚀 Server ready at ${url}`);

looks like this in Apollo Server 4:

apollo-server-4-standalone.ts
// npm install @apollo/server graphql
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { typeDefs, resolvers } from './schema';
interface MyContext {
token?: String;
}
const server = new ApolloServer<MyContext>({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, {
context: async ({ req }) => ({ token: req.headers.token }),
listen: { port: 4000 },
});
console.log(`🚀 Server ready at ${url}`);
apollo-server-4-standalone.js
// npm install @apollo/server graphql
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { typeDefs, resolvers } from './schema';
const server = new ApolloServer({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, {
context: async ({ req }) => ({ token: req.headers.token }),
listen: { port: 4000 },
});
console.log(`🚀 Server ready at ${url}`);

The startStandaloneServer function accepts two ; the first is the instance of ApolloServer that should begin listening for incoming requests. The second is an object for configuring your server's options, which most notably accepts the following properties:

Name /
Type
Description
context

Function

An optional context initialization function. The context function receives req and res options (see below for more details.).

In Apollo Server 3, you pass the context function to the constructor. In Apollo Server 4, you pass the context function to startStandaloneServer.

listen

Object

An optional listen configuration option. The listen option accepts an object with the same properties as the net.Server.listen options object.

For example, in Apollo Server 3, if you used server.listen(4321), you'll now pass listen: { port: 4321 } to the startStandaloneServer function in Apollo Server 4. If you didn't pass any arguments to Apollo Server 3's server.listen() method; you don't need to specify this listen option.

The startStandaloneServer function doesn't enable you to configure your server's CORS behavior. If you previously used the cors constructor option to customize your CORS settings in Apollo Server 3, use the expressMiddleware function in Apollo Server 4.

Similarly, if you used the stopGracePeriodMillis constructor option in Apollo Server 3, use the expressMiddleware function and specify stopGracePeriodMillis to the ApolloServerPluginDrainHttpServer plugin.

Migrate from apollo-server-express

If you used the apollo-server-express package in Apollo Server 3, use the expressMiddleware function in Apollo Server 4 (i.e., instead of using server.applyMiddleware or server.getMiddleware).

To migrate from Apollo Server 3's apollo-server-express package to using the expressMiddleware function, do the following:

  1. Install the @apollo/server and cors packages.
  2. Import symbols from @apollo/server (i.e., instead of from apollo-server-express and apollo-server-core).
  3. Add cors to your server setup.
  4. Remove the Apollo Server 3 apollo-server-express and apollo-server-core packages.
  5. If you are using apollo-server-express's default /graphql URL path (i.e., not specifying another URL with the path option), you can mount expressMiddleware at /graphql to maintain behavior. To use another URL path, mount your server (with app.use) at the specified path.

This Apollo Server 3 code:

apollo-server-3.ts
// npm install apollo-server-express apollo-server-core express graphql
import { ApolloServer } from 'apollo-server-express';
import { ApolloServerPluginDrainHttpServer } from 'apollo-server-core';
import express from 'express';
import http from 'http';
import { typeDefs, resolvers } from './schema';
interface MyContext {
token?: String;
}
const app = express();
const httpServer = http.createServer(app);
const server = new ApolloServer({
typeDefs,
resolvers,
context: async ({ req }) => ({ token: req.headers.token }),
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});
await server.start();
server.applyMiddleware({ app });
await new Promise<void>((resolve) => httpServer.listen({ port: 4000 }, resolve));
console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`);
apollo-server-3.js
// npm install apollo-server-express apollo-server-core express graphql
import { ApolloServer } from 'apollo-server-express';
import { ApolloServerPluginDrainHttpServer } from 'apollo-server-core';
import express from 'express';
import http from 'http';
import { typeDefs, resolvers } from './schema';
const app = express();
const httpServer = http.createServer(app);
const server = new ApolloServer({
typeDefs,
resolvers,
context: async ({ req }) => ({ token: req.headers.token }),
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});
await server.start();
server.applyMiddleware({ app });
await new Promise((resolve) => httpServer.listen({ port: 4000 }, resolve));
console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`);

looks like this in Apollo Server 4:

apollo-server-4.ts
// npm install @apollo/server express graphql cors
import { ApolloServer } from '@apollo/server';
import { expressMiddleware } from '@apollo/server/express4';
import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer';
import express from 'express';
import http from 'http';
import cors from 'cors';
import { typeDefs, resolvers } from './schema';
interface MyContext {
token?: String;
}
const app = express();
const httpServer = http.createServer(app);
const server = new ApolloServer<MyContext>({
typeDefs,
resolvers,
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});
await server.start();
app.use(
'/graphql',
cors<cors.CorsRequest>(),
express.json(),
expressMiddleware(server, {
context: async ({ req }) => ({ token: req.headers.token }),
}),
);
await new Promise<void>((resolve) => httpServer.listen({ port: 4000 }, resolve));
console.log(`🚀 Server ready at http://localhost:4000/graphql`);
apollo-server-4.js
// npm install @apollo/server express graphql cors
import { ApolloServer } from '@apollo/server';
import { expressMiddleware } from '@apollo/server/express4';
import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer';
import express from 'express';
import http from 'http';
import cors from 'cors';
import { typeDefs, resolvers } from './schema';
const app = express();
const httpServer = http.createServer(app);
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});
await server.start();
app.use(
'/graphql',
cors(),
express.json(),
expressMiddleware(server, {
context: async ({ req }) => ({ token: req.headers.token }),
}),
);
await new Promise((resolve) => httpServer.listen({ port: 4000 }, resolve));
console.log(`🚀 Server ready at http://localhost:4000/graphql`);

Removed integrations

Apollo Server 4 takes a fundamentally different approach to web framework integrations. By offering a well-defined API with a stable HTTP abstraction, Apollo Server 4 enables contributors to build and maintain integrations for the first time.

To that end, the Apollo Server core team no longer maintains the following integration packages in Apollo Server 4:

Instead, we now work with the broader community to maintain the following open-source integrations for Apollo Server:

If an integration doesn't exist for your framework, you can always build your own!

In Apollo Server 3, the apollo-server-express package supported both Express and its older predecessor Connect. In Apollo Server 4, expressMiddleware no longer supports Connect. An interested developer could build a Connect-specific middleware, and a PR to this migration guide is welcome if someone does this!

Packages merged into @apollo/server

As shown above, Apollo Server 4 combines the functionality of the apollo-server, apollo-server-express, and apollo-server-core packages into a new @apollo/server package.

But wait: there's more! The @apollo/server package also combines the following packages:

Plugins are in deep imports

In Apollo Server 3, the apollo-server-core package exports built-in plugins, like ApolloServerUsageReporting, at the top level. To use these plugins, you must install both the apollo-server-core package and the package you use to import ApolloServer (e.g., apollo-server or apollo-server-express).

In Apollo Server 4, these built-in plugins are part of the main @apollo/server package, which also imports the ApolloServer class. The @apollo/server package exports these built-in plugins with deep exports. This means you use deep imports for each built-in plugin, enabling you to evaluate only the plugin you use in your app and making it easier for bundlers to eliminate unused code.

There's one exception: the ApolloServerPluginLandingPageGraphQLPlayground plugin is now in its own package @apollo/server-plugin-landing-page-graphql-playground, which you can install separately.

This plugin installs the unmaintained Playground project as a landing page and is provided for compatibility with Apollo Server 2. This package will not be supported after Apollo Server 4 is released. We strongly recommend you switch to Apollo Server's 4's default landing page, which installs the actively maintained .

Apollo Server exports the following plugins:

PluginImport path
ApolloServerPluginCacheControl@apollo/server/plugin/cacheControl
ApolloServerPluginCacheControlDisabled@apollo/server/plugin/disabled
ApolloServerPluginDrainHttpServer@apollo/server/plugin/drainHttpServer
ApolloServerPluginInlineTrace@apollo/server/plugin/inlineTrace
ApolloServerPluginInlineTraceDisabled@apollo/server/plugin/disabled
ApolloServerPluginLandingPageDisabled@apollo/server/plugin/disabled
ApolloServerPluginLandingPageLocalDefault@apollo/server/plugin/landingPage/default
ApolloServerPluginLandingPageProductionDefault@apollo/server/plugin/landingPage/default
ApolloServerPluginSchemaReporting@apollo/server/plugin/schemaReporting
ApolloServerPluginUsageReporting@apollo/server/plugin/usageReporting
ApolloServerPluginUsageReportingDisabled@apollo/server/plugin/disabled

For example, replace this Apollo Server 3 code:

import { ApolloServerPluginUsageReporting } from 'apollo-server-core';

with this Apollo Server 4 code:

import { ApolloServerPluginUsageReporting } from '@apollo/server/plugin/usageReporting';

You can also import each plugin's associated TypeScript types (e.g., ApolloServerPluginUsageReportingOptions) from the same deep import as that plugin.

Once you've updated your imports, you can remove your project's dependency on apollo-server-core.

Known regressions

Appropriate 400 status codes

Apollo Server v4 responds to an invalid variables object with a 200 status code, whereas v3 responds appropriately with a 400 status code. This regression was introduced in PR #6502 and brought to our attention in Issue #7462.

Specifically, this regression affects cases where input variable coercion fails. of an incorrect type (i.e. String instead of Int) or unexpectedly null are examples that fail coercion. Additionally, missing or incorrect on input objects as well as custom that throw during validation will also fail variable coercion. For additional specifics on variable coercion, see the "Input Coercion" sections in the GraphQL spec.

We recommend mitigating this regression unless you've already modified your application to work around it. To do so, add the status400ForVariableCoercionErrors: true option to your ApolloServer constructor:

new ApolloServer({
// ...
status400ForVariableCoercionErrors: true,
});
new ApolloServer({
// ...
status400ForVariableCoercionErrors: true,
});

This option will no longer be needed (and will be ignored) in Apollo Server v5.

Bumped dependencies

Node.js

Apollo Server 4 supports Node.js 14.16.0 and later. (Apollo Server 3 supports Node.js 12.) This includes all LTS and Current major versions at the time of release.

If you're using Node.js 12, upgrade your runtime before upgrading to Apollo Server 4.

(Apollo Server 4 specifically requires v14.16.0 instead of merely v14.0.0, because that is the minimum version of Node.js 14 supported by our minimum supported version of graphql, as described in the next section.)

graphql

Apollo Server has a peer dependency on graphql (the core JS GraphQL implementation). Apollo Server 4 supports graphql v16.6.0 and later, but we strongly recommend using at least v16.7.0 due to a bug in graphql which can crash your server. (Apollo Server 3 supports graphql v15.3.0 through v16.)

If you're using an older version of graphql, upgrade it to a supported version before upgrading to Apollo Server 4.

Note that upgrading graphql may require you to upgrade other libraries that are installed in your project. For example, if you use Apollo Server with Apollo Gateway, you should upgrade Apollo Gateway to at least v0.50.1 or any v2.x version for full graphql 16 support before upgrading to Apollo Server 4.

TypeScript

If you use Apollo Server with TypeScript, you must use TypeScript v4.7.0 or newer.

For background, Apollo Server uses features introduced in v4.7. We want to put out "downleveled" versions of @apollo/server's type definitions for older versions of TypeScript, but have found TypeScript's typesVersions feature challenging to use.

If supporting older versions of TypeScript is important to you and you'd like to help us get typesVersions working, we'd appreciate PRs!

Removed constructor options

The following ApolloServer constructor options have been removed in favor of other features or configuration methods.

dataSources

📣 See our new Fetching Data article for more information on how the concept of a has changed in Apollo Sever 4.

In Apollo Server 3, the top-level dataSources constructor option essentially adds a post-processing step to your app's context function, creating DataSource subclasses and adding them to a dataSources on your context object. This means the TypeScript type the context function returns is different from the context type your and plugins receive. Additionally, this design obfuscates that DataSource objects are created once per request (i.e., like the rest of the context object).

Apollo Server 4 removes the dataSources constructor option. You can now treat DataSources like any other part of your context object.

In Apollo Server 3, immediately after constructing each DataSource subclass, Apollo Server invokes the dataSource.initialize({ cache, context }) function on each new DataSource. If you need to replicate this behavior in Apollo Server 4, you can pass the cache and context arguments to each DataSource constructor. In Apollo Server 4, you can find cache as a new readonly field on ApolloServer.

For example, below, we use the RESTDataSource class to create a DataSource with Apollo Server 3:

Apollo Server 3
import { RESTDataSource, RequestOptions } from 'apollo-datasource-rest';
import { ApolloServer } from 'apollo-server';
class MoviesAPI extends RESTDataSource {
override baseURL = 'https://movies-api.example.com/';
override willSendRequest(request: RequestOptions) {
request.headers.set('Authorization', this.context.token);
}
async getMovie(id: string): Promise<Movie> {
return this.get<Movie>(`movies/${encodeURIComponent(id)}`);
}
async updateMovie(movie: Movie): Promise<Movie> {
return this.patch(
'movies',
// Syntax for passing a request body
{ id: movie.id, movie },
);
}
}
interface ContextValue {
token: string;
dataSources: {
moviesAPI: MoviesAPI;
};
}
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req: ExpressRequest }): Omit<ContextValue, 'dataSources'> => {
return {
token: getTokenFromRequest(req),
};
},
dataSources: (): ContextValue['dataSources'] => {
return {
moviesAPI: new MoviesAPI(),
};
},
});
await server.listen();
Apollo Server 3
import { RESTDataSource } from 'apollo-datasource-rest';
import { ApolloServer } from 'apollo-server';
class MoviesAPI extends RESTDataSource {
baseURL = 'https://movies-api.example.com/';
willSendRequest(request) {
request.headers.set('Authorization', this.context.token);
}
async getMovie(id) {
return this.get(`movies/${encodeURIComponent(id)}`);
}
async updateMovie(movie) {
return this.patch(
'movies',
// Syntax for passing a request body
{ id: movie.id, movie },
);
}
}
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req: ExpressRequest }) => {
return {
token: getTokenFromRequest(req),
};
},
dataSources: () => {
return {
moviesAPI: new MoviesAPI(),
};
},
});
await server.listen();

Below is how you write the same code in Apollo Server 4.

Apollo Server 4
import { RESTDataSource, AugmentedRequest } from '@apollo/datasource-rest';
// KeyValueCache is the type of Apollo server's default cache
import type { KeyValueCache } from '@apollo/utils.keyvaluecache';
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
class MoviesAPI extends RESTDataSource {
override baseURL = 'https://movies-api.example.com/';
private token: string;
constructor(options: { token: string; cache: KeyValueCache }) {
super(options); // this sends our server's `cache` through
this.token = options.token;
}
override willSendRequest(path: string, request: AugmentedRequest) {
request.headers.authorization = this.token;
}
async getMovie(id: string): Promise<Movie> {
return this.get<Movie>(`movies/${encodeURIComponent(id)}`);
}
async updateMovie(movie: Movie): Promise<Movie> {
return this.patch(
'movies',
// Note the way we pass request bodies has also changed!
{ body: { id: movie.id, movie } },
);
}
}
interface ContextValue {
token: string;
dataSources: {
moviesAPI: MoviesAPI;
};
}
const server = new ApolloServer<ContextValue>({
typeDefs,
resolvers,
});
const { url } = await startStandaloneServer(server, {
context: async ({ req }) => {
const token = getTokenFromRequest(req);
const { cache } = server;
return {
token,
dataSources: {
moviesAPI: new MoviesAPI({ cache, token }),
},
};
},
});
console.log(`🚀 Server ready at ${url}`);
Apollo Server 4
import { RESTDataSource } from '@apollo/datasource-rest';
// KeyValueCache is the type of Apollo server's default cache
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
class MoviesAPI extends RESTDataSource {
baseURL = 'https://movies-api.example.com/';
constructor(options) {
super(options); // this sends our server's `cache` through
this.token = options.token;
}
willSendRequest(path, request) {
request.headers.authorization = this.token;
}
async getMovie(id) {
return this.get(`movies/${encodeURIComponent(id)}`);
}
async updateMovie(movie) {
return this.patch(
'movies',
// Note the way we pass request bodies has also changed!
{ body: { id: movie.id, movie } },
);
}
}
const server = new ApolloServer({
typeDefs,
resolvers,
});
const { url } = await startStandaloneServer(server, {
context: async ({ req }) => {
const token = getTokenFromRequest(req);
const { cache } = server;
return {
token,
dataSources: {
moviesAPI: new MoviesAPI({ cache, token }),
},
};
},
});
console.log(`🚀 Server ready at ${url}`);

In Apollo Server 4, we've moved apollo-datasource-rest to the new @apollo/datasource-rest package. Most of the functionality between the two packages is the same. However, some small syntax differences exist in how we pass a request's headers, params, cacheOptions, and body. See Fetching from REST for more details.

If you want to access your entire context's value within your DataSource, you can do so by making your context value a class (enabling it to refer to itself via this in its constructor):

import { RESTDataSource, WillSendRequestOptions } from '@apollo/datasource-rest';
import { KeyValueCache } from '@apollo/utils.keyvaluecache';
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { IncomingMessage } from 'http';
class MoviesAPI extends RESTDataSource {
override baseURL = 'https://movies-api.example.com/';
private contextValue: ContextValue;
constructor(options: { contextValue: ContextValue; cache: KeyValueCache }) {
super(options); // this should send `cache` through
this.contextValue = options.contextValue;
}
override willSendRequest(path: string, request: WillSendRequestOptions) {
request.headers['authorization'] = this.contextValue.token;
}
async getMovie(id): Promise<Movie> {
return this.get<Movie>(`movies/${encodeURIComponent(id)}`);
}
}
class ContextValue {
public token: string;
public dataSources: {
moviesAPI: MoviesAPI;
};
constructor({ req, server }: { req: IncomingMessage; server: ApolloServer<ContextValue> }) {
this.token = getTokenFromRequest(req);
const { cache } = server;
this.dataSources = {
moviesAPI: new MoviesAPI({ cache, contextValue: this }),
};
}
}
const server = new ApolloServer<ContextValue>({
typeDefs,
resolvers,
});
await startStandaloneServer(server, {
context: async ({ req }) => new ContextValue({ req, server }),
});
import { RESTDataSource } from '@apollo/datasource-rest';
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
class MoviesAPI extends RESTDataSource {
baseURL = 'https://movies-api.example.com/';
constructor(options) {
super(options); // this should send `cache` through
this.contextValue = options.contextValue;
}
willSendRequest(path, request) {
request.headers['authorization'] = this.contextValue.token;
}
async getMovie(id) {
return this.get(`movies/${encodeURIComponent(id)}`);
}
}
class ContextValue {
constructor({ req, server }) {
this.token = getTokenFromRequest(req);
const { cache } = server;
this.dataSources = {
moviesAPI: new MoviesAPI({ cache, contextValue: this }),
};
}
}
const server = new ApolloServer({
typeDefs,
resolvers,
});
await startStandaloneServer(server, {
context: async ({ req }) => new ContextValue({ req, server }),
});

If you want to migrate quickly to Apollo Server 4 without altering your , the snippet below replicates Apollo Server 3's dataSources behavior with a custom plugin.

We include this as a short-term fix and encourage you to create custom data source classes best suited for each source.

modules

In Apollo Server 3, there are several ways to provide your ApolloServer instance with a schema. One of the most common ways is to provide typeDefs and resolvers options (each of which can optionally be an array). Another way is using the modules option with an array of objects, each object containing typeDefs and resolvers keys. Under the hood, these two options use entirely different logic to do the same thing.

To simplify its API, Apollo Server 4 removes the modules constructor option. You can replace any previous usage of modules with the following syntax:

new ApolloServer({
typeDefs: modules.map({ typeDefs } => typeDefs),
resolvers: modules.map({ resolvers } => resolvers),
})

Additionally, the corresponding GraphQLSchemaModule TypeScript type is no longer exported.

mocks and mockEntireSchema

In Apollo Server 3, the mocks and mockEntireSchema constructor options enable Apollo Server to return simulated data for GraphQL based on your server's schema. Under the hood, Apollo Server 3's mocking functionality is provided via an outdated version of the @graphql-tools/mocks library.

Apollo Server 4 removes both the mocks and mockEntireSchema constructor options. You can instead directly incorporate the @graphql-tools/mock package into your app, enabling you to get the most up-to-date mocking features. For more details on configuring mocks, see the @graphql-tools/mocks docs.

The following examples compare the mocks and mockEntireSchema constructor options in Apollo Server 3 on the left and a replacement using @graphql-tools/mock on the right. You can also incrementally apply these changes in Apollo Server 3 without affecting behavior.

Apollo Server 3
new ApolloServer({
mocks: true,
});
Apollo Server 4
import { addMocksToSchema } from '@graphql-tools/mock';
import { makeExecutableSchema } from '@graphql-tools/schema';
new ApolloServer({
schema: addMocksToSchema({
schema: makeExecutableSchema({ typeDefs, resolvers }),
}),
});
Apollo Server 3
const mocks = {
Int: () => 6,
};
new ApolloServer({
mocks,
});
Apollo Server 4
import { addMocksToSchema } from '@graphql-tools/mock';
import { makeExecutableSchema } from '@graphql-tools/schema';
const mocks = {
Int: () => 6,
};
new ApolloServer({
schema: addMocksToSchema({
schema: makeExecutableSchema({ typeDefs, resolvers }),
mocks,
}),
});
Apollo Server 3
const mocks = {
Int: () => 6,
};
new ApolloServer({
mocks,
mockEntireSchema: false,
});
Apollo Server 4
import { addMocksToSchema } from '@graphql-tools/mock';
import { makeExecutableSchema } from '@graphql-tools/schema';
const mocks = {
Int: () => 6,
};
new ApolloServer({
schema: addMocksToSchema({
schema: makeExecutableSchema({ typeDefs, resolvers }),
mocks,
preserveResolvers: true,
}),
});

debug

In Apollo Server 3, the debug constructor option (which defaults to true unless the NODE_ENV environment variable is either production or test) controls several unrelated aspects of Apollo Server:

  • If debug is true, GraphQL responses with errors include stack traces.
  • If debug is true and ApolloServer uses the default logger, Apollo Server prints all DEBUG log-level messages.
    • Apollo Server 3 rarely sends messages at the DEBUG level, so this primarily affects plugins that use the provided logger to send DEBUG messages.
  • The debug flag is also available to plugins on GraphQLRequestContext to use as they wish.

Apollo Server 4 removes the debug constructor option. In its place is a new includeStacktraceInErrorResponses option which controls its namesake feature. Like debug, this option defaults to true unless the NODE_ENV environment variable is either production or test.

If you use debug in Apollo Server 3, you can use includeStacktraceInErrorResponses with the same value in Apollo Server 4:

const apolloServerInstance = new ApolloServer<MyContext>({
typeDefs,
resolvers,
includeStacktraceInErrorResponses: true,
});

Additionally, if your app or a plugin uses DEBUG-level log messages and your server doesn't use a custom logger, you are responsible for setting the default log level. For example, you can use the same Logger implementation that Apollo Server uses by default:

import loglevel from 'loglevel';
const logger = loglevel.getLogger('apollo-server');
logger.setLevel(shouldShowDebugMessages ? loglevel.levels