or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration.mderror-handling.mdgraphql-utilities.mdhttp-integration.mdindex.mdplugins.mdserver-core.md
tile.json

error-handling.mddocs/

Error Handling

Apollo Server Core provides a comprehensive error handling system with structured error types, formatting utilities, and categorization for different types of GraphQL and HTTP errors.

Capabilities

Apollo Error Classes

Base Apollo Server error classes for different types of application errors.

/**
 * Base Apollo Server error class
 */
class ApolloError extends Error {
  /** Error code for categorization */
  code?: string;
  
  /** Additional error properties */
  extensions: Record<string, any>;
  
  constructor(
    message: string,
    code?: string,
    extensions?: Record<string, any>
  );
}

/**
 * Authentication error - user not authenticated
 */
class AuthenticationError extends ApolloError {
  constructor(message: string, extensions?: Record<string, any>);
}

/**
 * Authorization error - user authenticated but not authorized
 */
class ForbiddenError extends ApolloError {
  constructor(message: string, extensions?: Record<string, any>);
}

/**
 * User input validation error
 */
class UserInputError extends ApolloError {
  constructor(message: string, extensions?: Record<string, any>);
}

/**
 * GraphQL syntax error
 */
class SyntaxError extends ApolloError {
  constructor(message: string, extensions?: Record<string, any>);
}

/**
 * GraphQL validation error
 */
class ValidationError extends ApolloError {
  constructor(message: string, extensions?: Record<string, any>);
}

Usage Examples:

import { 
  AuthenticationError, 
  ForbiddenError, 
  UserInputError 
} from "apollo-server-core";

const resolvers = {
  Query: {
    user: async (_, { id }, { user }) => {
      // Authentication check
      if (!user) {
        throw new AuthenticationError('You must be logged in to view user data');
      }
      
      // Authorization check
      if (user.id !== id && !user.isAdmin) {
        throw new ForbiddenError('You can only view your own user data');
      }
      
      // Input validation
      if (!id || typeof id !== 'string') {
        throw new UserInputError('Invalid user ID provided', {
          invalidArgs: ['id'],
        });
      }
      
      return getUserById(id);
    },
  },
};

Error Conversion and Formatting

Utilities for converting errors to ApolloError instances and formatting them for responses.

/**
 * Convert any error to ApolloError instance
 * @param error - Error to convert
 * @returns ApolloError instance
 */
function toApolloError(error: Error | ApolloError): ApolloError;

/**
 * Format Apollo errors for GraphQL response
 * @param errors - Array of GraphQL errors
 * @returns Array of formatted errors
 */
function formatApolloErrors(
  errors: readonly GraphQLError[]
): GraphQLFormattedError[];

Error Formatting Example:

import { toApolloError, formatApolloErrors } from "apollo-server-core";

// Convert database error to Apollo error
try {
  await database.query('SELECT * FROM users');
} catch (dbError) {
  const apolloError = toApolloError(dbError);
  apolloError.extensions.code = 'DATABASE_ERROR';
  throw apolloError;
}

// Custom error formatting in server config
const server = new ApolloServerBase({
  typeDefs,
  resolvers,
  formatError: (error) => {
    // Log the error
    console.error('GraphQL Error:', error);
    
    // Remove sensitive information in production
    if (process.env.NODE_ENV === 'production') {
      delete error.extensions?.exception?.stacktrace;
    }
    
    return error;
  },
});

HTTP Query Errors

Specific error types for HTTP request processing.

/**
 * Error thrown during HTTP query processing
 */
class HttpQueryError extends Error {
  /** HTTP status code for the error */
  statusCode: number;
  
  /** Error headers to include in response */
  headers?: Record<string, string>;
  
  /** Whether error is a GraphQL error (safe to expose) */
  isGraphQLError: boolean;
  
  constructor(
    statusCode: number,
    message: string,
    isGraphQLError?: boolean,
    headers?: Record<string, string>
  );
}

/**
 * Type guard to check if error is HttpQueryError
 * @param error - Error to check
 * @returns true if error is HttpQueryError
 */
function isHttpQueryError(error: any): error is HttpQueryError;

HTTP Error Handling Example:

import { runHttpQuery, isHttpQueryError } from "apollo-server-core";

async function handleGraphQLRequest(req, res) {
  try {
    const response = await runHttpQuery({
      method: req.method,
      query: req.body,
      options: { schema, context: { req } },
      request: req,
    });
    
    res.status(200).json(JSON.parse(response.graphqlResponse));
  } catch (error) {
    if (isHttpQueryError(error)) {
      // Handle HTTP-specific errors
      res.status(error.statusCode);
      if (error.headers) {
        res.set(error.headers);
      }
      
      if (error.isGraphQLError) {
        // Safe to expose GraphQL error details
        res.json({ errors: [{ message: error.message }] });
      } else {
        // Generic error response
        res.send(error.message);
      }
    } else {
      // Unexpected error
      console.error('Unexpected error:', error);
      res.status(500).send('Internal Server Error');
    }
  }
}

Error Extension and Customization

Advanced error handling patterns for custom error types and extensions.

interface GraphQLFormattedError {
  /** Error message */
  message: string;
  
  /** Source locations of the error */
  locations?: readonly SourceLocation[];
  
  /** Path to the field that caused the error */
  path?: readonly (string | number)[];
  
  /** Additional error information */
  extensions?: Record<string, any>;
}

interface SourceLocation {
  line: number;
  column: number;
}

Custom Error Types Example:

import { ApolloError } from "apollo-server-core";

// Custom error for business logic
class InsufficientFundsError extends ApolloError {
  constructor(availableBalance: number, requestedAmount: number) {
    super(`Insufficient funds: available ${availableBalance}, requested ${requestedAmount}`, 'INSUFFICIENT_FUNDS');
    
    this.extensions = {
      ...this.extensions,
      availableBalance,
      requestedAmount,
      code: 'INSUFFICIENT_FUNDS',
    };
  }
}

// Custom error for external service failures
class ExternalServiceError extends ApolloError {
  constructor(serviceName: string, originalError?: Error) {
    super(`External service ${serviceName} is unavailable`, 'SERVICE_UNAVAILABLE');
    
    this.extensions = {
      ...this.extensions,
      serviceName,
      code: 'SERVICE_UNAVAILABLE',
      originalError: originalError?.message,
    };
  }
}

// Usage in resolvers
const resolvers = {
  Mutation: {
    transferFunds: async (_, { fromAccount, toAccount, amount }) => {
      const balance = await getAccountBalance(fromAccount);
      
      if (balance < amount) {
        throw new InsufficientFundsError(balance, amount);
      }
      
      try {
        return await externalBankingService.transfer(fromAccount, toAccount, amount);
      } catch (error) {
        throw new ExternalServiceError('banking-service', error);
      }
    },
  },
};

Error Handling Best Practices

Recommended patterns for error handling in Apollo Server applications.

Centralized Error Logging:

const server = new ApolloServerBase({
  typeDefs,
  resolvers,
  formatError: (error) => {
    // Structured logging
    const errorInfo = {
      message: error.message,
      code: error.extensions?.code,
      path: error.path,
      timestamp: new Date().toISOString(),
      source: error.source?.body,
      positions: error.positions,
    };
    
    // Log based on error type
    if (error.extensions?.code === 'INTERNAL_ERROR') {
      console.error('Internal server error:', errorInfo);
    } else {
      console.warn('GraphQL error:', errorInfo);
    }
    
    // Return sanitized error for production
    if (process.env.NODE_ENV === 'production' && 
        error.extensions?.code === 'INTERNAL_ERROR') {
      return new Error('Internal server error');
    }
    
    return error;
  },
});

Plugin-based Error Handling:

const errorLoggingPlugin = {
  requestDidStart() {
    return {
      didEncounterErrors(requestContext) {
        // Access to full request context for error analysis
        const { request, errors, response } = requestContext;
        
        errors.forEach(error => {
          console.error('GraphQL execution error:', {
            query: request.query,
            variables: request.variables,
            operationName: request.operationName,
            error: error.message,
            path: error.path,
            extensions: error.extensions,
          });
        });
      },
    };
  },
};

const server = new ApolloServerBase({
  typeDefs,
  resolvers,
  plugins: [errorLoggingPlugin],
});

Error Response Structure

Standard structure for GraphQL error responses.

interface GraphQLErrorResponse {
  /** Query result data (may be partial or null) */
  data?: any | null;
  
  /** Array of errors that occurred */
  errors: GraphQLFormattedError[];
  
  /** Additional response extensions */
  extensions?: Record<string, any>;
}

Complete Error Handling Example:

import { 
  ApolloServerBase,
  AuthenticationError,
  UserInputError,
  formatApolloErrors
} from "apollo-server-core";

const server = new ApolloServerBase({
  typeDefs,
  resolvers: {
    Query: {
      sensitiveData: (_, __, { user }) => {
        if (!user) {
          throw new AuthenticationError('Authentication required');
        }
        return getSensitiveData();
      },
    },
    
    Mutation: {
      updateUser: (_, { input }) => {
        // Validate input
        if (!input.email || !input.email.includes('@')) {
          throw new UserInputError('Invalid email address', {
            invalidArgs: ['email'],
            providedValue: input.email,
          });
        }
        
        return updateUser(input);
      },
    },
  },
  
  formatError: (error) => {
    // Enhance error with additional context
    const formattedError = {
      ...error,
      extensions: {
        ...error.extensions,
        timestamp: new Date().toISOString(),
      },
    };
    
    // Log errors for monitoring
    if (error.extensions?.code !== 'BAD_USER_INPUT') {
      console.error('GraphQL Error:', formattedError);
    }
    
    return formattedError;
  },
});