or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration.mderror-handling.mdindex.mdlink-class.mdlink-creation.md
tile.json

error-handling.mddocs/

Error Handling

Apollo Link HTTP provides comprehensive error handling with categorized error types for different failure scenarios. The error system distinguishes between network errors, server errors, parsing errors, and client-side errors, enabling precise error handling strategies.

Capabilities

Error Types

Apollo Link HTTP defines several specific error types for different failure scenarios.

/**
 * Server error for HTTP responses with status >= 300
 * Thrown when the HTTP request succeeds but returns an error status
 */
type ServerError = Error & {
  /** The Response object from the fetch request */
  response: Response;
  /** The parsed response body (if parseable) */
  result: Record<string, any>;
  /** HTTP status code from the response */
  statusCode: number;
  /** Error name is always 'ServerError' */
  name: 'ServerError';
};

/**
 * Server parse error when response body cannot be parsed as JSON
 * Thrown when the response is received but JSON parsing fails
 */
type ServerParseError = Error & {
  /** The Response object from the fetch request */
  response: Response;
  /** HTTP status code from the response */
  statusCode: number;
  /** Raw response body text that failed to parse */
  bodyText: string;
  /** Error name is always 'ServerParseError' */
  name: 'ServerParseError';
};

/**
 * Invariant error from ts-invariant package
 * Base error class for client-side validation errors
 */
interface InvariantError extends Error {
  name: 'Invariant Violation';
}

/**
 * Client parse error when request parameters cannot be serialized
 * Thrown when variables or extensions cannot be JSON stringified
 */
type ClientParseError = InvariantError & {
  /** The original parsing error that caused this error */
  parseError: Error;
};

Error Scenarios

Network Errors:

  • Connection failures, timeouts, or DNS resolution issues
  • These are standard fetch errors and maintain their original error structure

Server Errors (HTTP Status >= 300):

  • Authentication failures (401 Unauthorized)
  • Authorization failures (403 Forbidden)
  • Not found errors (404 Not Found)
  • Server errors (500 Internal Server Error)
  • Rate limiting (429 Too Many Requests)

Server Parse Errors:

  • Invalid JSON in response body
  • Empty or malformed responses
  • Content-Type mismatches

Client Parse Errors:

  • Invalid variables that cannot be JSON serialized
  • Invalid extensions that cannot be JSON serialized
  • Circular references in request data

Error Handling Examples

Basic Error Handling with onError Link:

import { onError } from "apollo-link-error";
import { createHttpLink } from "apollo-link-http";
import { from } from "apollo-link";

const httpLink = createHttpLink({
  uri: "https://api.example.com/graphql",
});

const errorLink = onError(({ graphQLErrors, networkError, operation, forward }) => {
  // Handle GraphQL errors
  if (graphQLErrors) {
    graphQLErrors.forEach(({ message, locations, path }) => {
      console.error(`GraphQL error: ${message}`, { locations, path });
    });
  }

  // Handle network errors (including HTTP errors)
  if (networkError) {
    console.error("Network error:", networkError);
    
    // Handle specific HTTP error types
    if (networkError.name === 'ServerError') {
      const serverError = networkError as ServerError;
      console.error(`HTTP ${serverError.statusCode}:`, serverError.result);
      
      // Handle authentication errors
      if (serverError.statusCode === 401) {
        // Redirect to login or refresh token
        handleAuthenticationError();
      }
    }
    
    if (networkError.name === 'ServerParseError') {
      const parseError = networkError as ServerParseError;
      console.error(`Parse error (${parseError.statusCode}):`, parseError.bodyText);
    }
  }
});

const link = from([errorLink, httpLink]);

Conditional Error Handling:

import { createHttpLink } from "apollo-link-http";

const httpLink = createHttpLink({
  uri: "https://api.example.com/graphql",
});

const executeQuery = async () => {
  try {
    const result = await client.query({
      query: MY_QUERY,
      errorPolicy: "all", // Return both data and errors
    });
    
    return result;
  } catch (error) {
    // Handle different error types
    if (error.networkError) {
      const networkError = error.networkError;
      
      if (networkError.name === 'ServerError') {
        const serverError = networkError as ServerError;
        
        switch (serverError.statusCode) {
          case 401:
            throw new Error("Authentication required");
          case 403:
            throw new Error("Access denied");
          case 404:
            throw new Error("Resource not found");
          case 429:
            throw new Error("Rate limit exceeded");
          default:
            throw new Error(`Server error: ${serverError.statusCode}`);
        }
      }
      
      if (networkError.name === 'ServerParseError') {
        throw new Error("Invalid server response format");
      }
    }
    
    throw error;
  }
};

Mixed Success/Error Responses

Apollo Link HTTP handles cases where a response contains both data and errors (common in GraphQL):

// Example: Partial success response
const response = {
  data: {
    user: { name: "Alice" },
    posts: null, // This field failed
  },
  errors: [
    {
      message: "Access denied to posts",
      path: ["posts"],
      extensions: { code: "UNAUTHORIZED" }
    }
  ]
};

// The HTTP link will:
// 1. Call observer.next(response) to provide available data
// 2. Call observer.error(error) to signal the error condition
// This allows UI components to show partial data while handling errors

Request Cancellation

Apollo Link HTTP supports request cancellation using AbortController:

import { createHttpLink } from "apollo-link-http";

const httpLink = createHttpLink({
  uri: "https://api.example.com/graphql",
});

// Automatic cancellation support
const observable = httpLink.request(operation);

const subscription = observable.subscribe({
  next: (result) => console.log(result),
  error: (error) => {
    if (error.name === 'AbortError') {
      console.log('Request was cancelled');
    } else {
      console.error('Request failed:', error);
    }
  },
});

// Cancel the request
subscription.unsubscribe(); // Triggers AbortController.abort()

Custom Error Handling

You can implement custom error handling by extending the link chain:

import { ApolloLink } from "apollo-link";
import { createHttpLink } from "apollo-link-http";

const customErrorLink = new ApolloLink((operation, forward) => {
  return forward(operation).map((response) => {
    // Custom response processing
    if (response.errors) {
      // Transform or log errors as needed
      response.errors = response.errors.map((error) => ({
        ...error,
        timestamp: new Date().toISOString(),
      }));
    }
    
    return response;
  });
});

const httpLink = createHttpLink({
  uri: "https://api.example.com/graphql",
});

const link = customErrorLink.concat(httpLink);

Error Context Information

Errors include contextual information about the failed operation:

const errorLink = onError(({ networkError, operation }) => {
  if (networkError && networkError.name === 'ServerError') {
    const serverError = networkError as ServerError;
    
    console.error("Server error details:", {
      operationName: operation.operationName,
      variables: operation.variables,
      statusCode: serverError.statusCode,
      response: serverError.result,
      query: operation.query.loc?.source.body,
    });
  }
});