or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

caching-performance.mdcontext-types.mderror-handling.mdindex.mdlogging-system.mdplugin-system.mdrequest-processing.mdresult-processing.mdschema-management.mdserver-configuration.mdsubscription-system.md
tile.json

error-handling.mddocs/

Error Handling

Comprehensive error handling system with configurable error masking for production environments, GraphQL error utilities, HTTP error response generation, and error classification functions.

Capabilities

Error Masking

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;
}

Error Handling Functions

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;

Error Type Guards

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[];

Error Creation Utilities

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;

HTTP Validation Error Plugin

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>
>;

Error Extensions

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
            }
          });
        });
      }
    }
  };
}