Comprehensive error handling for GraphQL operations and transport failures. Apollo Client provides structured error types, handling mechanisms, and policies for managing GraphQL errors, network failures, and protocol-level issues.
Main error class that combines GraphQL errors, network errors, and client errors.
/**
* Main error class for Apollo Client operations
* Combines GraphQL errors, network errors, and client-side errors
*/
class ApolloError extends Error {
/** Human-readable error message */
message: string;
/** GraphQL execution errors */
graphQLErrors: readonly GraphQLError[];
/** Protocol-level errors */
protocolErrors: readonly GraphQLError[];
/** Client-side errors */
clientErrors: readonly Error[];
/** Network transport error */
networkError: Error | null;
/** Additional error information */
extraInfo: any;
constructor(options: ErrorOptions);
}
interface ErrorOptions {
graphQLErrors?: readonly GraphQLError[];
protocolErrors?: readonly GraphQLError[];
clientErrors?: readonly Error[];
networkError?: Error | null;
extraInfo?: any;
}Usage Example:
import { ApolloError } from "@apollo/client/errors";
function handleApolloError(error: ApolloError) {
console.log('Error message:', error.message);
// Handle GraphQL errors
if (error.graphQLErrors.length > 0) {
error.graphQLErrors.forEach(({ message, locations, path, extensions }) => {
console.log(`GraphQL error: ${message}`);
console.log('Location:', locations);
console.log('Path:', path);
console.log('Extensions:', extensions);
});
}
// Handle network errors
if (error.networkError) {
console.log('Network error:', error.networkError.message);
if (error.networkError instanceof ServerError) {
console.log('Status code:', error.networkError.statusCode);
console.log('Response:', error.networkError.result);
}
}
// Handle protocol errors
if (error.protocolErrors.length > 0) {
console.log('Protocol errors:', error.protocolErrors);
}
}Errors related to HTTP server responses and parsing.
/**
* HTTP server error with response details
* Thrown when server returns non-2xx status codes
*/
class ServerError extends Error {
/** HTTP response object */
response: Response;
/** Parsed response body */
result: Record<string, any>;
/** HTTP status code */
statusCode: number;
constructor(response: Response, result: any, message?: string);
}
/**
* JSON parsing error for server responses
* Thrown when server response cannot be parsed as JSON
*/
class ServerParseError extends Error {
/** HTTP response object */
response: Response;
/** Raw response body text */
bodyText: string;
/** HTTP status code */
statusCode: number;
constructor(response: Response, bodyText: string, message?: string);
}Usage Example:
import { ServerError, ServerParseError } from "@apollo/client/errors";
// In error handling
if (error.networkError instanceof ServerError) {
switch (error.networkError.statusCode) {
case 401:
// Handle unauthorized
redirectToLogin();
break;
case 403:
// Handle forbidden
showAccessDeniedMessage();
break;
case 500:
// Handle server error
showServerErrorMessage();
break;
default:
console.log('Server error:', error.networkError.result);
}
}
if (error.networkError instanceof ServerParseError) {
console.log('Failed to parse server response');
console.log('Raw response:', error.networkError.bodyText);
}Errors specific to the link layer and transport.
/**
* Error thrown by links in the link chain
* Used for transport-specific and middleware errors
*/
class LinkError extends Error {
constructor(message?: string, link?: ApolloLink);
}
/**
* Register a link-specific error for debugging
* @param error - Error to register
* @param link - Link that threw the error
*/
function registerLinkError(error: Error, link?: ApolloLink): void;
/**
* Local state resolver error
* Thrown when local state resolvers fail
*/
class LocalStateError extends Error {
constructor(message: string);
}Utility functions for error detection and handling.
/**
* Check if value is error-like (has message property)
* @param value - Value to check
* @returns True if value is error-like
*/
function isErrorLike(value: any): value is { message: string };
/**
* Convert any value to error-like object
* @param error - Value to convert
* @returns Error-like object
*/
function toErrorLike(error: unknown): { message: string };
/**
* Check if GraphQL result has protocol errors
* @param result - GraphQL result to check
* @returns True if result has protocol errors
*/
function graphQLResultHasProtocolErrors<T extends {}>(
result: T
): result is T & { extensions: Record<string | symbol, any> };
/**
* Symbol for protocol errors in result extensions
*/
const PROTOCOL_ERRORS_SYMBOL: unique symbol;Configuration options for how to handle different types of errors.
/**
* Error policy for GraphQL operations
* Controls how GraphQL errors are handled and exposed
*/
type ErrorPolicy = 'none' | 'ignore' | 'all';
// none: Treat GraphQL errors as runtime errors (default)
// ignore: Ignore GraphQL errors and return partial data
// all: Return both data and errorsUsage Example:
// Different error policies in action
const { data, error } = useQuery(GET_USER, {
variables: { id: '1' },
errorPolicy: 'all' // Return both data and errors
});
// With 'all' policy, you might get partial data even with errors
if (data) {
// Render what data you have
renderUser(data.user);
}
if (error) {
// Handle errors separately
handleErrors(error.graphQLErrors);
}
// Global error policy configuration
const client = new ApolloClient({
// ... other options
defaultOptions: {
query: {
errorPolicy: 'ignore' // Ignore errors globally for queries
},
mutate: {
errorPolicy: 'none' // Treat errors as exceptions for mutations
}
}
});Using ErrorLink for centralized error handling.
import { ErrorLink } from "@apollo/client/link/error";
/**
* Error handler function for ErrorLink
* @param errorResponse - Error response with context
* @returns Observable to retry or void to continue
*/
type ErrorHandler = (errorResponse: ErrorResponse) => Observable<FetchResult> | void;
interface ErrorResponse {
/** GraphQL errors */
graphQLErrors?: readonly GraphQLError[];
/** Network error */
networkError?: Error;
/** Current operation being executed */
operation: Operation;
/** Forward function to retry operation */
forward: NextLink;
/** Response object (if available) */
response?: ExecutionResult;
}Usage Example:
import { ErrorLink } from "@apollo/client/link/error";
import { fromPromise } from "@apollo/client/link";
const errorLink = new ErrorLink(({ graphQLErrors, networkError, operation, forward }) => {
if (graphQLErrors) {
graphQLErrors.forEach(({ message, locations, path, extensions }) => {
console.log(`GraphQL error: Message: ${message}, Location: ${locations}, Path: ${path}`);
// Handle specific error codes
if (extensions?.code === 'UNAUTHENTICATED') {
// Clear stored tokens
localStorage.removeItem('token');
// Redirect to login
window.location.href = '/login';
}
});
}
if (networkError) {
console.log(`Network error: ${networkError}`);
// Retry logic for network errors
if (networkError.statusCode === 401) {
// Token might be expired, try to refresh
return fromPromise(
refreshAuthToken().then(() => {
// Retry the operation with new token
return forward(operation);
}).catch(() => {
// Refresh failed, redirect to login
window.location.href = '/login';
return;
})
);
}
}
});
// Use in link chain
const link = from([
errorLink,
httpLink
]);Advanced error combination classes for complex error scenarios.
/**
* Combined GraphQL errors from multiple sources
* Aggregates multiple GraphQL errors into single error
*/
class CombinedGraphQLErrors extends Error {
constructor(errors: readonly GraphQLError[]);
/** Check if errors object is CombinedGraphQLErrors */
static is(errors: any): errors is CombinedGraphQLErrors;
}
/**
* Combined protocol errors from transport layer
* Aggregates protocol-level errors
*/
class CombinedProtocolErrors extends Error {
constructor(errors: readonly GraphQLError[]);
/** Check if errors object is CombinedProtocolErrors */
static is(errors: any): errors is CombinedProtocolErrors;
}
/**
* Wrapper for non-standard error types
* Ensures all errors have consistent interface
*/
class UnconventionalError extends Error {
constructor(error: unknown);
}Usage Example:
// Handling combined errors
function handleCombinedErrors(error: ApolloError) {
if (CombinedGraphQLErrors.is(error.graphQLErrors)) {
console.log('Multiple GraphQL errors occurred');
error.graphQLErrors.forEach(err => {
console.log('Individual error:', err.message);
});
}
if (error.protocolErrors && CombinedProtocolErrors.is(error.protocolErrors)) {
console.log('Multiple protocol errors occurred');
// Handle protocol-specific error recovery
}
}// Comprehensive error handling in components
function MyComponent() {
const { data, loading, error } = useQuery(GET_DATA, {
errorPolicy: 'all',
onError: (error) => {
// Component-level error handling
if (error.networkError) {
showToast('Network error occurred', 'error');
}
if (error.graphQLErrors.length > 0) {
const hasAuthError = error.graphQLErrors.some(
err => err.extensions?.code === 'UNAUTHENTICATED'
);
if (hasAuthError) {
redirectToLogin();
}
}
}
});
if (loading) return <Spinner />;
// With errorPolicy: 'all', we might have both data and errors
if (error && !data) {
return <ErrorDisplay error={error} />;
}
return (
<div>
{data && <DataDisplay data={data} />}
{error && <ErrorBanner errors={error.graphQLErrors} />}
</div>
);
}interface GraphQLError {
message: string;
locations?: readonly SourceLocation[];
path?: readonly (string | number)[];
nodes?: readonly ASTNode[];
source?: Source;
positions?: readonly number[];
originalError?: Error;
extensions?: Record<string, any>;
}
interface ExecutionResult<TData = Record<string, any>, TExtensions = Record<string, any>> {
errors?: readonly GraphQLError[];
data?: TData | null;
extensions?: TExtensions;
}
type NextLink = (operation: Operation) => Observable<FetchResult>;