Comprehensive error handling system with configurable error masking for production environments, GraphQL error utilities, HTTP error response generation, and error classification functions.
Core error masking functionality for hiding sensitive information in production environments.
/**
* Error masking function type
*/
type MaskError = (error: unknown, message: string, isDev?: boolean) => Error;
/**
* Default error masking implementation
* @param error - Original error to mask
* @param message - Masked error message
* @param isDev - Whether in development mode
* @returns Masked error instance
*/
function maskError(error: unknown, message: string, isDev?: boolean): Error;
/**
* Error masking configuration options
*/
interface YogaMaskedErrorOpts {
/** Custom error masking function */
maskError?: MaskError;
/** Default masked error message */
errorMessage?: string;
/** Whether in development mode */
isDev?: boolean;
}Main error handling utilities for processing and categorizing errors.
/**
* Handle and process errors with masking and logging
* @param error - Error to handle
* @param maskedErrorsOpts - Error masking configuration
* @param logger - Logger instance
* @returns Array of GraphQL errors
*/
function handleError(
error: unknown,
maskedErrorsOpts: YogaMaskedErrorOpts | null,
logger: YogaLogger
): GraphQLError[];
/**
* Generate HTTP response init based on GraphQL execution result errors
* @param result - GraphQL execution result
* @param headers - Additional response headers
* @returns Response initialization object
*/
function getResponseInitByRespectingErrors(
result: ExecutionResult,
headers?: Record<string, string>
): ResponseInit;Type guard functions for identifying different types of errors.
/**
* Check if value is a GraphQL error
* @param val - Value to check
* @returns True if value is GraphQLError
*/
function isGraphQLError(val: unknown): val is GraphQLError;
/**
* Check if error is an original GraphQL error (not wrapped)
* @param error - GraphQL error to check
* @returns True if error is original
*/
function isOriginalGraphQLError(error: GraphQLError): boolean;
/**
* Check if error is an abort error (request cancellation)
* @param error - Error to check
* @returns True if error is abort error
*/
function isAbortError(error: unknown): error is DOMException;
/**
* Check if object is an array of GraphQL errors
* @param obj - Object to check
* @returns True if object is GraphQL errors array
*/
function areGraphQLErrors(obj: unknown): obj is readonly GraphQLError[];Re-exported utilities for creating GraphQL errors.
/**
* Create a GraphQL error with proper formatting (from @graphql-tools/utils)
* @param message - Error message
* @param options - Error options
* @returns GraphQL error instance
*/
function createGraphQLError(
message: string,
options?: {
nodes?: ReadonlyArray<ASTNode>;
source?: Source;
positions?: ReadonlyArray<number>;
path?: ReadonlyArray<string | number>;
originalError?: Error;
extensions?: Record<string, any>;
}
): GraphQLError;Plugin for handling HTTP validation errors with proper status codes.
/**
* Plugin for HTTP validation error handling
* @returns Plugin instance
*/
function useHTTPValidationError<TServerContext>(): Plugin<
Record<string, any> & YogaInitialContext,
TServerContext,
Record<string, any>
>;Interface for GraphQL HTTP extensions in error responses.
/**
* GraphQL HTTP extensions for error responses
*/
interface GraphQLHTTPExtensions {
/** HTTP status code */
status?: number;
/** Additional HTTP headers */
headers?: Record<string, string>;
}Usage Examples:
import {
createYoga,
maskError,
handleError,
isGraphQLError,
isAbortError,
createGraphQLError,
useHTTPValidationError,
getResponseInitByRespectingErrors
} from 'graphql-yoga';
// Basic error masking setup
const yoga = createYoga({
schema: mySchema,
maskedErrors: {
maskError: (error, message, isDev) => {
if (isDev) {
return new Error(`Development: ${error.message}`);
}
return new Error(message);
},
errorMessage: 'Something went wrong',
isDev: process.env.NODE_ENV !== 'production'
},
plugins: [
useHTTPValidationError()
]
});
// Custom error handling plugin
function useCustomErrorHandling(): Plugin {
return {
onExecutionResult({ result, setResult }) {
if (result.errors) {
const processedErrors = result.errors.map(error => {
// Log different types of errors differently
if (isGraphQLError(error)) {
console.error('GraphQL Error:', {
message: error.message,
path: error.path,
locations: error.locations,
extensions: error.extensions
});
}
// Create user-friendly error messages
if (error.message.includes('Database')) {
return createGraphQLError('Service temporarily unavailable', {
extensions: {
code: 'SERVICE_ERROR',
status: 503
}
});
}
if (error.message.includes('Unauthorized')) {
return createGraphQLError('Authentication required', {
extensions: {
code: 'UNAUTHENTICATED',
status: 401
}
});
}
return error;
});
setResult({
...result,
errors: processedErrors
});
}
}
};
}
// Error categorization
function categorizeError(error: unknown): string {
if (isAbortError(error)) {
return 'REQUEST_CANCELLED';
}
if (isGraphQLError(error)) {
return error.extensions?.code || 'GRAPHQL_ERROR';
}
if (error instanceof TypeError) {
return 'TYPE_ERROR';
}
if (error instanceof ReferenceError) {
return 'REFERENCE_ERROR';
}
return 'UNKNOWN_ERROR';
}
// Advanced error handling with logging
const advancedErrorYoga = createYoga({
schema: mySchema,
maskedErrors: {
maskError: (error, message, isDev) => {
const category = categorizeError(error);
const errorId = generateErrorId();
// Log with category and ID for tracking
console.error(`Error [${errorId}] - ${category}:`, error);
if (isDev) {
return new Error(`[${errorId}] ${error.message}`);
}
return new Error(`[${errorId}] ${message}`);
},
isDev: process.env.NODE_ENV !== 'production'
},
plugins: [
{
onExecutionResult({ result, setResult }) {
if (result.errors) {
// Add error tracking IDs
const errorsWithIds = result.errors.map(error => {
const errorId = generateErrorId();
return createGraphQLError(error.message, {
...error,
extensions: {
...error.extensions,
errorId,
timestamp: new Date().toISOString()
}
});
});
setResult({
...result,
errors: errorsWithIds
});
}
}
},
useHTTPValidationError()
]
});
// Custom error classes
class ValidationError extends Error {
constructor(message: string, public field: string) {
super(message);
this.name = 'ValidationError';
}
}
class AuthenticationError extends Error {
constructor(message: string = 'Authentication required') {
super(message);
this.name = 'AuthenticationError';
}
}
class AuthorizationError extends Error {
constructor(message: string = 'Insufficient permissions') {
super(message);
this.name = 'AuthorizationError';
}
}
// Error handling in resolvers
const resolverSchema = createSchema({
typeDefs: `
type Query {
user(id: ID!): User
secureData: String
}
type User {
id: ID!
name: String!
email: String!
}
`,
resolvers: {
Query: {
user: async (_, { id }, context) => {
try {
if (!id) {
throw new ValidationError('User ID is required', 'id');
}
const user = await context.userService.findById(id);
if (!user) {
throw createGraphQLError('User not found', {
extensions: {
code: 'USER_NOT_FOUND',
status: 404
}
});
}
return user;
} catch (error) {
if (error instanceof ValidationError) {
throw createGraphQLError(error.message, {
extensions: {
code: 'VALIDATION_ERROR',
field: error.field,
status: 400
}
});
}
throw error;
}
},
secureData: (_, __, context) => {
if (!context.user) {
throw new AuthenticationError();
}
if (!context.user.hasPermission('READ_SECURE_DATA')) {
throw new AuthorizationError();
}
return 'This is secure data';
}
}
}
});
// Global error handling with custom response headers
const globalErrorYoga = createYoga({
schema: resolverSchema,
plugins: [
{
onResultProcess({ result, setResult, fetchAPI }) {
if (typeof result === 'object' && 'errors' in result && result.errors) {
const responseInit = getResponseInitByRespectingErrors(result, {
'X-Error-Count': result.errors.length.toString(),
'X-Has-Errors': 'true'
});
const response = new fetchAPI.Response(
JSON.stringify(result),
{
...responseInit,
headers: {
'Content-Type': 'application/json',
...responseInit.headers
}
}
);
setResult(response);
}
}
}
]
});
// Error monitoring integration
function useErrorMonitoring(): Plugin {
return {
onExecutionResult({ result }) {
if (result.errors) {
result.errors.forEach(error => {
// Send to error monitoring service
errorMonitoringService.captureException(error, {
tags: {
component: 'graphql-yoga',
operation: result.extensions?.operationName
},
extra: {
path: error.path,
locations: error.locations
}
});
});
}
}
};
}