From 5fe597cf21037710e40cf6c97977a1955b14de6a Mon Sep 17 00:00:00 2001 From: Stephen Barlow Date: Fri, 12 Feb 2021 15:28:23 -0800 Subject: [PATCH 1/3] Content edit of error reporting article --- docs/source/data/errors.md | 273 --------------------- docs/source/data/errors.mdx | 474 ++++++++++++++++++++++++++++++++++++ 2 files changed, 474 insertions(+), 273 deletions(-) delete mode 100644 docs/source/data/errors.md create mode 100644 docs/source/data/errors.mdx diff --git a/docs/source/data/errors.md b/docs/source/data/errors.md deleted file mode 100644 index 7f12270475b..00000000000 --- a/docs/source/data/errors.md +++ /dev/null @@ -1,273 +0,0 @@ ---- -title: Error handling -description: Making errors actionable on the client and server ---- - -Apollo Server provides a collection of predefined errors, including -`AuthenticationError`, `ForbiddenError`, `UserInputError`, and a generic -`ApolloError`. These errors are designed to enhance errors thrown before and during GraphQL execution, making it easier to debug your Apollo Server integration and enabling clients to take specific actions based on an error. - -When an error occurs in Apollo Server both inside and outside of resolvers, each error inside of the `errors` array contains an `extensions` object that contains the information added by Apollo Server. - -## Default information - -The first step to improving the usability of a server is providing the error stack trace by default. The following example demonstrates the response returned from Apollo Server with a resolver that throws a node [`SystemError`](https://nodejs.org/api/errors.html#errors_system_errors). - -```js{14-16} -const { - ApolloServer, - gql, -} = require('apollo-server'); - -const typeDefs = gql` - type Query { - readError: String - } -`; - -const resolvers = { - Query: { - readError: (parent, args, context) => { - fs.readFileSync('/does/not/exist'); - }, - }, -}; -``` - -The response will return: - -![Screenshot demonstrating an error stacktrace and additional](../images/features/error-stacktrace.png) - -> To disable stacktraces for production, pass `debug: false` to the Apollo Server constructor or set the `NODE_ENV` environment variable to 'production' or 'test'. Note that this will make the stacktrace unavailable to your application. If you want to log the stacktrace, but not send it in the response to the client, see [Masking and logging errors](#masking-and-logging-errors) below. - -## Codes - -In addition to stacktraces, Apollo Server's exported errors specify a human-readable string in the `code` field of `extensions` that enables the client to perform corrective actions. In addition to improving the client experience, the `code` field allows the server to categorize errors. For example, an `AuthenticationError` sets the code to `UNAUTHENTICATED`, which enables the client to reauthenticate and would generally be ignored as a server anomaly. - -```js{4,15-17} -const { - ApolloServer, - gql, - AuthenticationError, -} = require('apollo-server'); - -const typeDefs = gql` - type Query { - authenticationError: String - } -`; - -const resolvers = { - Query: { - authenticationError: (parent, args, context) => { - throw new AuthenticationError('must authenticate'); - }, - }, -}; -``` - -The response will return: - -![Screenshot demonstrating unauthenticated error code](../images/features/error-code.png) - -## Augmenting error details - -When clients provide bad input, you may want to return additional information -like a localized message for each field or argument that was invalid. The -following example demonstrates how you can use `UserInputError` to augment -your error messages with additional details. - -```js{15-21} -const { - ApolloServer, - UserInputError, - gql, -} = require('apollo-server'); - -const typeDefs = gql` - type Mutation { - userInputError(input: String): String - } -`; - -const resolvers = { - Mutation: { - userInputError: (parent, args, context, info) => { - if (args.input !== 'expected') { - throw new UserInputError('Form Arguments invalid', { - invalidArgs: Object.keys(args), - }); - } - }, - }, -}; -``` - -The response will return: - -![Screenshot demonstrating augmented error](../images/features/error-user-input.png) - -## Other errors - -If you need to define other error codes that are specific to your -application, you can use the base `ApolloError` class. - -```js -new ApolloError(message, code, additionalProperties); -``` - -## Masking and logging errors - -### For the client response - -The Apollo Server constructor accepts a `formatError` function that is run on each error passed back to the client. This can be used to mask errors as well as for logging. - -> Note that while this changes the error which is sent to the client, it doesn't change the error that is sent to Apollo Studio. See the `rewriteError` function in [For Apollo Studio reporting](#for-apollo-studio-reporting) below if this behavior is desired. - -This example demonstrates throwing a different error when the error's message starts with `Database Error: `: - -```js{4-10} -const server = new ApolloServer({ - typeDefs, - resolvers, - 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, so 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 within the resolver. This can be used to `instanceof` check against a specific error class, such as `AuthenticationError`, `ValidationError`, etc.: - -```js - /* ... */ - 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` (e.g. localization or personalization), consider using the `didEncounterErrors` lifecycle hook to attach additional properties to the error, which can be accessed and utilized within `formatError`. - -### For Apollo Studio reporting - -You can use Apollo Studio to analyze error rates instead of simply logging errors to the console. If you connect Apollo Server 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 account for these needs, a `rewriteError` function can be passed to the [usage reporting plugin](../api/plugin/usage-reporting/). (The usage reporting plugin is installed automatically with its default configuration when you specify an Apollo API key; you will need to install it explicitly in your `ApolloServer` in order to configure `rewriteError`, as shown below.) At a high-level, the function will receive the error to be reported (i.e., a `GraphQLError` or an `ApolloError`) as the first argument. The function should then return a modified form of that error (e.g., after changing the `err.message` to remove potentially sensitive information), or should return an explicit `null` in order to avoid reporting the error entirely. - -(For federated services, instead of setting `rewriteError` in the usage reporting plugin in the gateway's `ApolloServer`, set [`rewriteError` in the inline trace plugin](../api/plugin/inline-trace/#rewriteerror) in the implementing service's `ApolloServer`.) - -The following sections give some examples of various use-cases for `rewriteError`. - -#### Avoid reporting lower-severity, predefined errors - -If an application is using the predefined errors noted above (`AuthenticationError`, `ForbiddenError`, `UserInputError`, etc.), these can be used with `rewriteError`. - -For example, if the current server is `throw`ing the `AuthenticationError` -when an incorrect password is supplied, an implementor can avoid reporting -this to Apollo Studio by defining `rewriteError` as follows: - -```js{7-17} -const { ApolloServer, AuthenticationError } = require("apollo-server"); -const { ApolloServerPluginUsageReporting } = require("apollo-server-core"); -const server = new ApolloServer({ - typeDefs, - resolvers, - 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; - } - }), - ], -}); -``` - -This example configuration ensures that any `AuthenticationError` that's thrown within a resolver is only reported to the client, and never sent to Apollo Studio. All other errors are transmitted to Studio normally. - -#### Avoid reporting an error based on other properties - -When generating an error (e.g., `new ApolloError("Failure!")`), the `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). These properties can be checked when determining whether an error should be reported to Apollo Studio using the `rewriteError` function as follows: - -```js{7-17} -const { ApolloServer } = require("apollo-server"); -const { ApolloServerPluginUsageReporting } = require("apollo-server-core"); -const server = new ApolloServer({ - typeDefs, - resolvers, - 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; - } - }), - ], -}); -``` - -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. - -#### 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 be used to ensure -such information is not sent to Apollo Studio and potentially revealed outside -its intended scope: - -```js{7-13} -const { ApolloServer } = require("apollo-server"); -const { ApolloServerPluginUsageReporting } = require("apollo-server-core"); -const server = new ApolloServer({ - typeDefs, - resolvers, - 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; - } - }), - ], -}); -``` - -In this case, the error above is reported to Apollo Studio as: - -``` -The REDACTED doesn't have sufficient privileges. -``` diff --git a/docs/source/data/errors.mdx b/docs/source/data/errors.mdx new file mode 100644 index 00000000000..83a285c9f67 --- /dev/null +++ b/docs/source/data/errors.mdx @@ -0,0 +1,474 @@ +--- +title: Error handling +description: Making errors actionable on the client and server +--- + +import {ExpansionPanel} from 'gatsby-theme-apollo-docs'; + +Whenever Apollo Server encounters errors while processing a GraphQL operation, its response to the client includes an `errors` array that contains each error that occurred. Each error in the array has an `extensions` field that provides additional useful information, including an error `code` and an `exception.stacktrace`. + +Here's an example error response caused by misspelling the `__typename` field in a query: + + + + +```json +{ + "errors":[ + { + "message":"Cannot query field \"__typenam\" on type \"Query\".", + "locations":[ + { + "line":1, + "column":2 + } + ], + "extensions":{ + "code":"GRAPHQL_VALIDATION_FAILED", + "exception":{ + "stacktrace":[ + "GraphQLError: Cannot query field \"__typenam\" on type \"Query\".", + " at Object.Field (/my_project/node_modules/graphql/validation/rules/FieldsOnCorrectTypeRule.js:48:31)", + " ...additional lines...", + ] + } + } + } + ] +} +``` + + + +To help with debugging, Apollo Server defines error subclasses that represent different types of errors that can occur while handling a GraphQL operation (such as `SyntaxError` and `ValidationError`). These subclasses each return a different [error code](#error-codes), 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 [the `apollo-server-errors` package](https://github.com/apollographql/apollo-server/blob/main/packages/apollo-server-errors/src/index.ts). You can also [create your own custom errors and codes](#custom-errors). + +## Error codes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Code /
Subclass
Description
+ +###### `GRAPHQL_PARSE_FAILED` + +`SyntaxError` + + +The GraphQL operation string contains a syntax error. +
+ +###### `GRAPHQL_VALIDATION_FAILED` + +`ValidationError` + + +The GraphQL operation is not valid against the server's schema. +
+ +###### `BAD_USER_INPUT` + +`UserInputError` + + +The GraphQL operation includes an invalid value for a field argument. +
+ +###### `UNAUTHENTICATED` + +`AuthenticationError` + + +The server failed to authenticate with a required data source, such as a REST API. +
+ +###### `FORBIDDEN` + +`ForbiddenError` + + +The server was unauthorized to access a required data source, such as a REST API. +
+ +###### `PERSISTED_QUERY_NOT_FOUND` + +`PersistedQueryNotFoundError` + + +A client sent the hash of a query string to execute via [automatic persisted queries](../performance/apq/), but the query was not in the APQ cache. +
+ +###### `PERSISTED_QUERY_NOT_SUPPORTED` + +`PersistedQueryNotSupportedError` + + +A client sent the hash of a query string to execute via [automatic persisted queries](../performance/apq/), but the server has disabled APQ. +
+ +###### `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 + +Apollo Server throws errors of most built-in types automatically when applicable. For example, it throws a `ValidationError` whenever an incoming operation isn't valid against the server's schema. + +Your resolvers can also throw errors in situations where Apollo Server doesn't do so automatically. + +For example, this resolver throws a [`UserInputError`](#bad_user_input) if the integer value provided for a user's ID is less than `1`: + + + +```js{20-25} +const { + ApolloServer, + gql, + UserInputError +} = require('apollo-server'); + +const typeDefs = gql` + type Query { + userWithID(id: ID!): User + } + + type User { + id: ID! + name: String! + } +`; + +const resolvers = { + Query: { + userWithID: (parent, args, context) => { + if (args.id + +If a resolver throws an error that is _not_ an `ApolloError` instance, that error is converted to a generic `ApolloError` with an `extensions` field that includes a `stacktrace` and `code` (specifically [`INTERNAL_SERVER_ERROR`](#internal_server_error)), along with other relevant error details. + +### Including custom error details + +Whenever you throw an `ApolloError`, you can add arbitrary fields to the error's `extensions` object to provide additional context to the client. You specify these fields in an object you provide to the error's constructor. + +This example builds on the one above by adding the name of the GraphQL argument that was invalid: + + + +```js{22-24} +const { + ApolloServer, + gql, + UserInputError +} = require('apollo-server'); + +const typeDefs = gql` + type Query { + userWithID(id: ID!): User + } + + type User { + id: ID! + name: String! + } +`; + +const resolvers = { + Query: { + userWithID: (parent, args, context) => { + if (args.id + +This results in a response like the following: + + + +```json{15} +{ + "errors": [ + { + "message": "Invalid argument value", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "userWithID" + ], + "extensions": { + "argumentName": "id", + "code": "BAD_USER_INPUT", + "exception": { + "argumentName": "id", + "stacktrace": [ + "UserInputError: Invalid argument value", + " at userWithID (/my-project/index.js:25:13)", + " ...more lines...", + ] + } + } + } + ] +} +``` + + + +## 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 + +```ts +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 + +```ts +import { ApolloError } from 'apollo-server-errors'; + +throw new ApolloError('My error message', 'MY_ERROR_CODE', myCustomDetailsObject); +``` + + +## Masking and logging errors + +> To disable stacktraces for production, pass `debug: false` to the Apollo Server constructor or set the `NODE_ENV` environment variable to 'production' or 'test'. Note that this will make the stacktrace unavailable to your application. If you want to log the stacktrace, but not send it in the response to the client, see [Masking and logging errors](#masking-and-logging-errors) below. + +### 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 metrics reporting. See [For Apollo Studio reporting](#for-apollo-studio-reporting). + +This example returns a more generic error whenever the original error's message begins with `Database Error: `: + +```js{4-12} +const server = new ApolloServer({ + typeDefs, + resolvers, + 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 resolver. You can use this property to obtain the `instanceof` the error class, such as `AuthenticationError` or `ValidationError`: + +```js + 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 [creating a plugin](../integrations/plugins/) 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 Apollo Server 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 [usage reporting plugin](../api/plugin/usage-reporting/). + +> The usage reporting plugin is installed automatically with its default configuration if you provide an Apollo API key to Apollo Server. 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 argument. 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 implementing service's [inline trace plugin](../api/plugin/inline-trace/#rewriteerror). Do not define it in the gateway. + +#### Example: Ignoring common low-severity errors + +Let's say our server is `throw`ing an `AuthenticationError` whenever an incorrect password is provided. We can avoid reporting these errors to Apollo Studio by defining `rewriteError`, like so: + +```js{7-17} +const { ApolloServer, AuthenticationError } = require("apollo-server"); +const { ApolloServerPluginUsageReporting } = require("apollo-server-core"); +const server = new ApolloServer({ + typeDefs, + resolvers, + 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; + } + }), + ], +}); +``` + +This example configuration ensures that any `AuthenticationError` that's thrown within a resolver 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: + +```js{7-17} +const { ApolloServer } = require("apollo-server"); +const { ApolloServerPluginUsageReporting } = require("apollo-server-core"); +const server = new ApolloServer({ + typeDefs, + resolvers, + 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; + } + }), + ], +}); +``` + +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: + +```js +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: + +```js{7-13} +const { ApolloServer } = require("apollo-server"); +const { ApolloServerPluginUsageReporting } = require("apollo-server-core"); +const server = new ApolloServer({ + typeDefs, + resolvers, + 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; + } + }), + ], +}); +``` + +In this case, the error above is reported to Apollo Studio as: + +``` +The REDACTED doesn't have sufficient privileges. +``` From 8e3ecee5921978d6c60623a4da52c94c067343c5 Mon Sep 17 00:00:00 2001 From: Stephen Barlow Date: Fri, 26 Feb 2021 13:03:31 -0800 Subject: [PATCH 2/3] Incorporate feedback from glasser --- docs/source/data/errors.mdx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/source/data/errors.mdx b/docs/source/data/errors.mdx index 83a285c9f67..2a15d9eafa4 100644 --- a/docs/source/data/errors.mdx +++ b/docs/source/data/errors.mdx @@ -5,7 +5,7 @@ description: Making errors actionable on the client and server import {ExpansionPanel} from 'gatsby-theme-apollo-docs'; -Whenever Apollo Server encounters errors while processing a GraphQL operation, its response to the client includes an `errors` array that contains each error that occurred. Each error in the array has an `extensions` field that provides additional useful information, including an error `code` and an `exception.stacktrace`. +Whenever Apollo Server encounters errors while processing a GraphQL operation, its response to the client includes an `errors` array that contains each error that occurred. Each error in the array has an `extensions` field 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` field in a query: @@ -317,10 +317,19 @@ import { ApolloError } from 'apollo-server-errors'; throw new ApolloError('My error message', 'MY_ERROR_CODE', myCustomDetailsObject); ``` +## Omitting or including `stacktrace` + +The `exception.stacktrace` error field is useful while developing and debugging your server, but you probably don't want to expose it to clients in production. + +By default, Apollo Server _omits_ the `exception.stacktrace` field 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 [the constructor of `ApolloServer`](../api/apollo-server/#constructor). 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 `stacktrace`s without including them in responses to clients, see [Masking and logging errors](#masking-and-logging-errors). ## Masking and logging errors -> To disable stacktraces for production, pass `debug: false` to the Apollo Server constructor or set the `NODE_ENV` environment variable to 'production' or 'test'. Note that this will make the stacktrace unavailable to your application. If you want to log the stacktrace, but not send it in the response to the client, see [Masking and logging errors](#masking-and-logging-errors) below. +You can edit Apollo Server 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 From 4937478be30584f3747945bfbafcfe69ef65f46d Mon Sep 17 00:00:00 2001 From: Stephen Barlow Date: Mon, 1 Mar 2021 13:46:26 -0800 Subject: [PATCH 3/3] Incorporate feedback from trevor-scheer --- docs/source/data/errors.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/data/errors.mdx b/docs/source/data/errors.mdx index 2a15d9eafa4..d4ee7aeef8d 100644 --- a/docs/source/data/errors.mdx +++ b/docs/source/data/errors.mdx @@ -197,7 +197,7 @@ const typeDefs = gql` const resolvers = { Query: { userWithID: (parent, args, context) => { - if (args.id { - if (args.id { // Don't give the specific errors to the client. - if (err.message.startsWith("Database Error: ")) { + if (err.message.startsWith('Database Error: ')) { return new Error('Internal server error'); } @@ -370,7 +370,7 @@ The error instance received by `formatError` (a `GraphQLError`) contains an `ori }, ``` -> To make context-specific adjustments to the error received by `formatError` (such as localization or personalization), consider [creating a plugin](../integrations/plugins/) that uses the `didEncounterErrors` lifecycle event to attach additional properties to the error. These properties can be accessed from `formatError`. +> To make context-specific adjustments to the error received by `formatError` (such as localization or personalization), consider [creating a plugin](../integrations/plugins/) that uses the [`didEncounterErrors` lifecycle event](../integrations/plugins/#didencountererrors) to attach additional properties to the error. These properties can be accessed from `formatError`. ### For Apollo Studio reporting