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.
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;
};Network Errors:
Server Errors (HTTP Status >= 300):
Server Parse Errors:
Client Parse Errors:
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;
}
};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 errorsApollo 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()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);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,
});
}
});