Allow `formatError` to be an async function
As proposed here: https://github.com/apollographql/apollo-server/pull/5689
It's reasonable for formatError to be an async function. If anything, not making it so in v4 was an oversight. We can expand its return type to PromiseOrValue<T> for this major version and align on our pattern of only Promise<T> in the next major version.
Please update formatError to provide request context as well. Handling all errors in a uniform context-specific way is, essentially, impractical now,
- Errors thrown before context creation are not surfaced to the unusual "requestDidStart" and "willSendResponse" functions. If needed, context-specific details need to be carefully defined in the error object and remapped into the response using "formatError",
- Errors thrown during validation come with no context information but are surfaced to the unusual "requestDidStart" and "willSendResponse" functions. If needed, context-specific details must be re-associated with the error using those,
- Errors thrown during field resolution can have access to context from the resolver and also through "requestDidStart" and "willSendResponse"
Ideally, a function like this would be great, if implemented nicely
{
formatErrorALL: async (ctx, originalError, formattedError) => { /* ... */ }
}
The main challenge in providing the context value to the formatError function is that it is also used to format errors that arise before context creation, such as errors that come from bad requests.
Apollo prevents applications from returning internationalized error messages. It prevents applications from adequately linking an error response with its originating request. For example, if context-creation throws an error for a request with header "content-language": "de-DE", Apollo prevents the application from matching the error to the "de-DE" header to return a German-language error response.
One might think "maybe the application could throw its own error messages and internationalize them?" Or, "maybe there is a hook the application could use, to transform error responses using request details?" These approaches seem to sort-of work until, frustratingly, one realizes that Apollo throws its own English language errors and that Apollo does not provide hooks to re-associate all errors with their request details.
Apollo calls only one hook for all error responses and this hook does not include request details: formatError: (formattedError, error) => ({ /*...*/ })
Application-instantiated errors can be internationalized around request details, but apollo-instantiated errors, such as validation errors, will not get this treatment. Request details cannot be recovered later by the application for some errors.
Two hooks available for processing error messages are requestDidStart () { return { willSendResponse(context) {} }} and contextCreationDidFail. These hooks are only called for a limited range of errors that occur during validation and field-resolution. These hooks can be deceptive because they catch many errors and one might think the problem is solved until one day Apollo starts returning errors that aren't surfaced to these hooks.
Apollo makes things extra difficult because it erases details from the error, unless the error is constructed in this non-standard way https://www.apollographql.com/docs/apollo-server/data/errors/#custom-errors Details stored on the error are lost and missing when the error surfaces to the hooks described above. Please stop mutating or dropping errors surfaced from withing the application. If apollo needs its own special error details that's fine but its not necessary to throw out or remove data that might be needed by the application.
const server = new ApolloServer({
gateway,
introspection: true,
plugins: [],
formatError: async ({context, **maskError**}),
cache: "",
context: async ({ req }) => ({})
});
This would be quite useful, rather than creating plugin as its a constantly used feature.