Fully-featured GraphQL Server with focus on easy setup, performance & great developer experience
Comprehensive error handling system with masking, GraphQL error creation, and production-ready error processing. Provides secure error reporting while maintaining useful debugging information in development.
Function to create standardized GraphQL errors with proper extensions and metadata.
/**
* Creates a GraphQL-compliant error with optional metadata
* @param message - Error message
* @param options - Optional error configuration
* @returns GraphQL error instance
*/
function createGraphQLError(
message: string,
options?: {
nodes?: readonly ASTNode[];
source?: Source;
positions?: readonly number[];
path?: readonly (string | number)[];
originalError?: Error;
extensions?: GraphQLErrorExtensions;
}
): GraphQLError;Usage Examples:
import { createGraphQLError } from "graphql-yoga";
// Basic error
const error = createGraphQLError("User not found");
// Error with extensions
const authError = createGraphQLError("Authentication required", {
extensions: {
code: 'UNAUTHENTICATED',
http: { status: 401 }
}
});
// Error with path and original error
const validationError = createGraphQLError("Invalid input", {
path: ['user', 'email'],
originalError: new Error('Email format invalid'),
extensions: {
field: 'email',
code: 'VALIDATION_ERROR'
}
});
// Use in resolvers
const resolvers = {
Query: {
user: (_, { id }) => {
if (!id) {
throw createGraphQLError("User ID is required", {
extensions: { code: 'BAD_USER_INPUT' }
});
}
const user = findUser(id);
if (!user) {
throw createGraphQLError("User not found", {
extensions: {
code: 'NOT_FOUND',
userId: id
}
});
}
return user;
}
}
};Function to mask sensitive error information for production environments.
/**
* Masks error details to prevent information leakage
* @param error - Original error
* @param message - Fallback error message
* @param isDev - Whether running in development mode
* @returns Masked error
*/
function maskError(error: unknown, message: string, isDev?: boolean): Error;
/**
* Error masking function type
*/
type MaskError = (error: unknown, message: string, isDev?: boolean) => Error;Usage Examples:
import { maskError } from "graphql-yoga";
// Basic error masking
const maskedError = maskError(
new Error("Database connection failed with credentials: admin:secret123"),
"Internal server error",
false // production mode
);
// Result: Error with message "Internal server error"
// Development mode
const devError = maskError(
new Error("Database connection failed"),
"Internal server error",
true // development mode
);
// Result: Original error preserved for debugging
// Custom masking in server configuration
const yoga = createYoga({
maskedErrors: {
errorMessage: 'Something went wrong',
isDev: process.env.NODE_ENV === 'development',
maskError: (error, message, isDev) => {
// Custom masking logic
if (isDev && error instanceof Error) {
return error; // Show full error in development
}
if (error instanceof ValidationError) {
return new Error('Validation failed'); // Custom message for validation errors
}
return new Error(message); // Default masked message
}
}
});Comprehensive error processing function that handles various error types and formats.
/**
* Processes and formats various error types into GraphQL errors
* @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[];
/**
* Error masking configuration
*/
interface YogaMaskedErrorOpts {
/** Custom error masking function */
maskError: MaskError;
/** Default error message for masked errors */
errorMessage: string;
/** Whether running in development mode */
isDev?: boolean;
}Usage Examples:
import { handleError } from "graphql-yoga";
// Error handling in custom plugin
const errorHandlingPlugin: Plugin = {
onResultProcess({ result, setResult }) {
if ('errors' in result && result.errors) {
const processedErrors = result.errors.map(error =>
handleError(error, maskedErrorsOpts, logger)
).flat();
setResult({
...result,
errors: processedErrors
});
}
}
};
// Custom error handling configuration
const maskedErrorsOpts: YogaMaskedErrorOpts = {
errorMessage: 'An unexpected error occurred',
isDev: process.env.NODE_ENV === 'development',
maskError: (error, message, isDev) => {
if (isDev) {
console.error('Development error:', error);
return error instanceof Error ? error : new Error(String(error));
}
// Log original error but return masked message
if (error instanceof Error) {
console.error('Production error masked:', error.message);
}
return new Error(message);
}
};Utility functions for identifying and working with different error types.
/**
* Check if value is a GraphQL error
* @param val - Value to check
* @returns Whether value is GraphQL error
*/
function isGraphQLError(val: unknown): val is GraphQLError;
/**
* Check if value is an original GraphQL error (not wrapped)
* @param val - Value to check
* @returns Whether value is original GraphQL error
*/
function isOriginalGraphQLError(
val: unknown
): val is GraphQLError & { originalError: GraphQLError };
/**
* Check if object is an array of GraphQL errors
* @param obj - Object to check
* @returns Whether object is GraphQL error array
*/
function areGraphQLErrors(obj: unknown): obj is readonly GraphQLError[];Usage Examples:
import { isGraphQLError, isOriginalGraphQLError, areGraphQLErrors } from "graphql-yoga";
// Error type checking
function processError(error: unknown) {
if (isGraphQLError(error)) {
console.log('GraphQL error:', error.message);
console.log('Extensions:', error.extensions);
if (isOriginalGraphQLError(error)) {
console.log('Original error:', error.originalError);
}
} else {
console.log('Non-GraphQL error:', error);
}
}
// Processing error arrays
function processErrors(errors: unknown) {
if (areGraphQLErrors(errors)) {
errors.forEach((error, index) => {
console.log(`Error ${index}:`, error.message);
});
}
}
// Use in resolver error handling
const resolvers = {
Query: {
sensitiveData: async (_, __, context) => {
try {
return await fetchSensitiveData(context.userId);
} catch (error) {
if (isGraphQLError(error)) {
throw error; // Re-throw GraphQL errors as-is
}
// Convert other errors to GraphQL errors
throw createGraphQLError("Failed to fetch data", {
originalError: error instanceof Error ? error : new Error(String(error)),
extensions: { code: 'FETCH_ERROR' }
});
}
}
}
};Function to generate HTTP response configuration based on GraphQL errors.
/**
* Generates HTTP response configuration based on GraphQL errors
* @param result - GraphQL execution result
* @param headers - Additional headers to include
* @param isApplicationJson - Whether response is JSON
* @returns Response configuration with status and headers
*/
function getResponseInitByRespectingErrors(
result: ResultProcessorInput,
headers?: Record<string, string>,
isApplicationJson?: boolean
): {
status: number;
headers: Record<string, string>;
};Usage Examples:
import { getResponseInitByRespectingErrors } from "graphql-yoga";
// Custom result processor using error-aware response
const customProcessor: ResultProcessor = (result, fetchAPI) => {
const responseInit = getResponseInitByRespectingErrors(result, {
'X-Custom-Header': 'value'
}, true);
return new fetchAPI.Response(
JSON.stringify(result),
{
status: responseInit.status,
headers: {
'Content-Type': 'application/json',
...responseInit.headers
}
}
);
};
// Error-aware response in plugin
const responsePlugin: Plugin = {
onResultProcess({ result, setResultProcessor }) {
setResultProcessor((result, fetchAPI) => {
const { status, headers } = getResponseInitByRespectingErrors(result);
// Add custom error tracking
if (status >= 400) {
headers['X-Error-Tracked'] = 'true';
logError(result);
}
return new fetchAPI.Response(JSON.stringify(result), {
status,
headers: {
'Content-Type': 'application/json',
...headers
}
});
}, 'application/json');
}
};Extended error information for HTTP-specific and Yoga-specific metadata.
/**
* GraphQL error extensions with HTTP metadata
*/
interface GraphQLHTTPExtensions {
/** Whether error follows GraphQL spec */
spec?: boolean;
/** HTTP status code */
status?: number;
/** HTTP headers to set */
headers?: Record<string, string>;
}
/**
* Extended GraphQL error extensions
*/
declare module 'graphql' {
interface GraphQLErrorExtensions {
/** HTTP-specific extensions */
http?: GraphQLHTTPExtensions;
/** Whether error was unexpected */
unexpected?: boolean;
}
}Usage Examples:
// Error with HTTP extensions
const httpError = createGraphQLError("Unauthorized", {
extensions: {
code: 'UNAUTHORIZED',
http: {
status: 401,
headers: {
'WWW-Authenticate': 'Bearer'
}
}
}
});
// Error with custom extensions
const businessError = createGraphQLError("Insufficient funds", {
extensions: {
code: 'BUSINESS_LOGIC_ERROR',
category: 'payment',
retryable: false,
http: {
status: 422
}
}
});
// Use in resolvers
const resolvers = {
Mutation: {
processPayment: async (_, { amount }, context) => {
if (!context.user) {
throw createGraphQLError("Authentication required", {
extensions: {
code: 'UNAUTHENTICATED',
http: { status: 401 }
}
});
}
if (amount <= 0) {
throw createGraphQLError("Invalid amount", {
extensions: {
code: 'VALIDATION_ERROR',
field: 'amount',
http: { status: 400 }
}
});
}
try {
return await processPayment(amount, context.user);
} catch (error) {
throw createGraphQLError("Payment processing failed", {
originalError: error,
extensions: {
code: 'PAYMENT_ERROR',
http: { status: 502 }
}
});
}
}
}
};tessl i tessl/npm-graphql-yoga@4.0.0