Docs
Launch GraphOS Studio
Apollo Server 3 is officially deprecated, with end-of-life scheduled for 22 October 2024. Learn more about upgrading to a supported Apollo Server version.

Error handling

Making errors actionable on the client and server


Whenever encounters errors while processing a , its response to the client includes an errors array that contains each error that occurred. Each error in the array has an extensions that provides additional useful information, including an error code and (while in development mode) an exception.stacktrace.

Here's an example error response caused by misspelling the __typename in a :

To help with debugging, defines error subclasses that represent different types of errors that can occur while handling a (such as SyntaxError and ValidationError). These subclasses each return a different

, which enables requesting clients to respond differently to different error types.

These built-in error subclasses inherit from the generic ApolloError class, and they're all defined in

. You can also
create your own custom errors and codes
.

Error codes

Code /
Subclass
Description
GRAPHQL_PARSE_FAILED

SyntaxError

The string contains a syntax error.

GRAPHQL_VALIDATION_FAILED

ValidationError

The is not valid against the server's schema.

BAD_USER_INPUT

UserInputError

The includes an invalid value for a .

UNAUTHENTICATED

AuthenticationError

The server failed to authenticate with a required , such as a REST API.

FORBIDDEN

ForbiddenError

The server was unauthorized to access a required , such as a REST API.

PERSISTED_QUERY_NOT_FOUND

PersistedQueryNotFoundError

A client sent the hash of a string to execute via

, but the was not in the cache.

PERSISTED_QUERY_NOT_SUPPORTED

PersistedQueryNotSupportedError

A client sent the hash of a string to execute via

, but the server has disabled .

INTERNAL_SERVER_ERROR

None

An unspecified error occurred.

This is the default error code returned by any ApolloError instance that doesn't specify a different code.

Throwing errors

throws errors of most built-in types automatically when applicable. For example, it throws a ValidationError whenever an incoming isn't valid against the server's schema.

Your can also throw errors in situations where doesn't do so automatically.

For example, this throws a

if the integer value provided for a user's ID is less than 1:

If a throws an error that is not an ApolloError instance, that error is converted to a generic ApolloError with an extensions that includes a stacktrace and code (specifically

), along with other relevant error details.

Including custom error details

Whenever you throw an ApolloError, you can add arbitrary to the error's extensions object to provide additional context to the client. You specify these in an object you provide to the error's constructor.

This example builds on the one above by adding the name of the that was invalid:

This results in a response like the following:

Custom errors

You can create a custom error by defining your own subclass of ApolloError, or by initializing an ApolloError object directly:

Subclass with custom error code

import { ApolloError } from 'apollo-server-errors';
export class MyError extends ApolloError {
constructor(message: string) {
super(message, 'MY_ERROR_CODE');
Object.defineProperty(this, 'name', { value: 'MyError' });
}
}
throw new MyError('My error message')

Direct initialization

import { ApolloError } from 'apollo-server-errors';
throw new ApolloError('My error message', 'MY_ERROR_CODE', myCustomExtensions);

Omitting or including stacktrace

The exception.stacktrace error is useful while developing and debugging your server, but you probably don't want to expose it to clients in production.

By default, omits the exception.stacktrace if the NODE_ENV environment variable is set to either production or test.

You can override this default behavior by passing the debug option to

. If debug is true, exception.stacktrace is always included. If it's false, exception.stacktrace is always omitted.

Note that when exception.stacktrace is omitted, it's also unavailable to your application. To log error stacktraces without including them in responses to clients, see

.

Masking and logging errors

You can edit error details before they're passed to a client or reported to Apollo Studio. This enables you to omit sensitive or irrelevant data.

For client responses

The ApolloServer constructor accepts a formatError function that is run on each error before it's passed back to the client. You can use this function to mask particular errors, as well as for logging.

The formatError function does not modify errors that are sent to Apollo Studio as part of usage reporting. See

.

This example returns a more generic error whenever the original error's message begins with Database Error: :

const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
cache: "bounded",
plugins: [
ApolloServerPluginLandingPageLocalDefault({ embed: true }),
],
formatError: (err) => {
// Don't give the specific errors to the client.
if (err.message.startsWith('Database Error: ')) {
return new Error('Internal server error');
}
// Otherwise return the original error. The error can also
// be manipulated in other ways, as long as it's returned.
return err;
},
});
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});

The error instance received by formatError (a GraphQLError) contains an originalError property, which represents the original error thrown in the . You can use this property to obtain the instanceof the error class, such as AuthenticationError or ValidationError:

formatError(err) {
if (err.originalError instanceof AuthenticationError) {
return new Error('Different authentication error message!');
}
},

To make context-specific adjustments to the error received by formatError (such as localization or personalization), consider

that uses the
didEncounterErrors lifecycle event
to attach additional properties to the error. These properties can be accessed from formatError.

For Apollo Studio reporting

You can use Apollo Studio to analyze your server's error rates. If you connect to Studio, all errors are sent to Studio by default. If you don't want certain error information to be sent to Studio (either because the error is unimportant or because certain information is confidential), you can modify or redact errors entirely before they're transmitted.

To accomplish this, you can provide a rewriteError function to the

.

The usage reporting plugin is installed automatically with its default configuration if you provide an Apollo API key to . To define a custom rewriteError function, you need to install the plugin explicitly with a custom configuration, as shown in examples below.

Your rewriteError function is called for each error (a GraphQLError or an ApolloError) to be reported to Studio. The error is provided as the function's first . The function can either:

  • Return a modified form of the error (e.g., by changing the err.message to remove potentially sensitive information)
  • Return null to prevent the error from being reported entirely

For federated graphs, instead define rewriteError in each 's

. Do not define it in the gateway.

Example: Ignoring common low-severity errors

Let's say our server is throwing an AuthenticationError whenever an incorrect password is provided. We can avoid reporting these errors to Apollo Studio by defining rewriteError, like so:

const { ApolloServer, AuthenticationError } = require("apollo-server");
const {
ApolloServerPluginUsageReporting,
ApolloServerPluginLandingPageLocalDefault,
} = require('apollo-server-core');
const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
cache: "bounded",
plugins: [
ApolloServerPluginUsageReporting({
rewriteError(err) {
// Return `null` to avoid reporting `AuthenticationError`s
if (err instanceof AuthenticationError) {
return null;
}
// All other errors will be reported.
return err;
}
}),
ApolloServerPluginLandingPageLocalDefault({ embed: true }),
],
});

This example configuration ensures that any AuthenticationError that's thrown within a is only reported to the client, and never sent to Apollo Studio. All other errors are transmitted to Studio normally.

Example: Filtering errors based on other properties

When generating an error (e.g., new ApolloError("Failure!")), the error's message is the most common property (in this case it's Failure!). However, any number of properties can be attached to the error (such as a code property).

We can check these properties when determining whether an error should be reported to Apollo Studio using the rewriteError function as follows:

const { ApolloServer } = require("apollo-server");
const {
ApolloServerPluginUsageReporting,
ApolloServerPluginLandingPageLocalDefault,
} = require('apollo-server-core');
const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
cache: "bounded",
plugins: [
ApolloServerPluginUsageReporting({
rewriteError(err) {
// Using a more stable, known error property (e.g. `err.code`) would be
// more defensive, however checking the `message` might serve most needs!
if (err.message && err.message.startsWith("Known error message")) {
return null;
}
// All other errors should still be reported!
return err;
}
}),
ApolloServerPluginLandingPageLocalDefault({ embed: true }),
],
});

This example configuration ensures that any error that starts with Known error message is not transmitted to Apollo Studio, but all other errors are sent as normal.

Example: Redacting the error message

If it is necessary to change an error prior to reporting it to Apollo Studio (for example, if there is personally identifiable information in the error message), the rewriteError function can also help.

Consider an example where the error contains a piece of information like an API key:

throw new ApolloError("The x-api-key:12345 doesn't have sufficient privileges.");

The rewriteError function can ensure that such information is not sent to Apollo Studio and potentially revealed outside its intended scope:

const { ApolloServer } = require("apollo-server");
const {
ApolloServerPluginUsageReporting,
ApolloServerPluginLandingPageLocalDefault,
} = require('apollo-server-core');
const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
cache: "bounded",
plugins: [
ApolloServerPluginUsageReporting({
rewriteError(err) {
// Make sure that a specific pattern is removed from all error messages.
err.message = err.message.replace(/x-api-key:[A-Z0-9-]+/, "REDACTED");
return err;
}
}),
ApolloServerPluginLandingPageLocalDefault({ embed: true }),
],
});

In this case, the error above is reported to Apollo Studio as:

The REDACTED doesn't have sufficient privileges.

Returning HTTP status codes

, by design, does not use the same conventions from REST to communicate via HTTP verbs and status codes. Client information should be contained in the schema or as part of the standard response errors . We recommend using the included

or
Custom Errors
for error consistency rather than directly modifying the HTTP response.

You can set custom on your HTTP response by using a

. Be aware that libraries may not treat all response status codes the same, and so it will be up to your team to decide what patterns to use.

As an example, here is how you could set a custom response header and status code based on a error:

const setHttpPlugin = {
async requestDidStart() {
return {
async willSendResponse({ response }) {
response.http.headers.set('Custom-Header', 'hello');
if (response?.errors?.[0]?.message === 'teapot') {
response.http.status = 418;
}
}
};
}
};
const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
cache: 'bounded',
plugins: [
setHttpPlugin,
ApolloServerPluginLandingPageLocalDefault({ embed: true }),
],
});
Previous
Data sources
Next
File uploads
Edit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company