ApolloServerPluginErrorHandler severity mapping transition in Apollo Server 4.0
0 reputation · 03 Feb 2020, 22:09 UTC
0 reputation · 03 Feb 2020, 22:09 UTC
Developers building Apollo Server 4.0 applications can use the ApolloServerPluginErrorHandler to centralize error alerts and consolidate visibility across GraphQL operations. The plugin’s onError callback receives an ErrorContext object that includes a severity field, which defaults to 'error' and can be overridden to 'warning' or 'info' to reduce alert volume.
However, the plugin does not prescribe a consistent severity-mapping strategy for distinguishing GraphQL validation errors from runtime resolver exceptions. Additionally, the onError hook lacks built-in support for request-context-aware filtering, such as user authentication status or operation depth, which limits fine-grained noise reduction without custom logic.
What criteria should determine whether a validation failure is logged as 'info' while a resolver exception remains 'error'? Can the onError callback safely access the original request object to conditionally adjust severity based on user role? What is the recommended pattern for routing severity-adjusted alerts to external services like Sentry without introducing silent critical failures?
34775 reputation · 04 Feb 2020, 05:29 UTC
Treat GraphQL errors whose extensions.code starts with GRAPHQL_VALIDATION or GRAPHQL_PARSE as 'info' because they are client-fixable. Treat resolver exceptions and any error with INTERNAL_SERVER_ERROR or no code as 'error' so server bugs are not masked.
Yes. The context object in didEncounterErrors is the same object returned by your server's context function. Attach user or auth there during context construction and read context.user?.role to adjust severity per request. Avoid reading request.headers directly in the plugin for integration portability.
Never await Sentry or another transport inside the plugin hook. Push a sanitized payload to a bounded non-blocking queue, flush it on an interval, wrap each send in try/catch, and log locally on failure. Use Sentry.flush(timeout) in a process.on('beforeExit') handler rather than awaiting per request.
import { ApolloServerPlugin } from '@apollo/server';
export const severityPlugin: ApolloServerPlugin = {
async requestDidStart() {
return {
async didEncounterErrors({ errors, context }) {
for (const err of errors) {
const code = err.extensions?.code ?? '';
const isClientError =
code.startsWith('GRAPHQL_VALIDATION') ||
code.startsWith('GRAPHQL_PARSE');
err.extensions = {
...err.extensions,
severity: isClientError ? 'info' : 'error',
};
if (context.user?.role === 'admin' && isClientError) {
err.extensions.severity = 'warning';
}
}
},
};
},
};
import * as Sentry from '@sentry/node';
const queue: Array<{ error: Error; level: string }> = [];
setInterval(() => {
const batch = queue.splice(0, 100);
for (const item of batch) {
try {
Sentry.captureException(item.error, { level: item.level as Sentry.SeverityLevel });
} catch {
console.error('Sentry send failed', item.error);
}
}
}, 1000);
process.on('beforeExit', () => Sentry.flush(2000));
async willSendResponse({ response }) {
if (response.errors) {
const hasCritical = response.errors.some(
(e) => e.extensions?.severity === 'error'
);
if (!hasCritical) {
response.http.status = 200;
}
}
}
errors.map((e) => e.extensions?.code) in didEncounterErrors and compare a malformed query against a resolver-thrown error.Object.keys(context) inside didEncounterErrors to confirm user or request is present for your integration.One missing diagnostic detail can change the recommendation: which HTTP integration are you using (Express, Fastify, Lambda, standalone)? The shape of context.request differs by integration, and some middleware may short-circuit before didEncounterErrors runs.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.