GraphQL.js provides comprehensive error handling with source location tracking, path information, and response formatting capabilities.
The main error class for GraphQL operations with rich context information.
/**
* GraphQL error with location and context information
*/
class GraphQLError extends Error {
constructor(message: string, options?: GraphQLErrorOptions);
/** Error message */
readonly message: string;
/** Source locations where error occurred */
readonly locations?: ReadonlyArray<SourceLocation>;
/** Response path where error occurred */
readonly path?: ReadonlyArray<string | number>;
/** Source document */
readonly source?: Source;
/** Character positions in source */
readonly positions?: ReadonlyArray<number>;
/** Original error that was wrapped */
readonly originalError?: Error;
/** Additional error data */
readonly extensions?: GraphQLErrorExtensions;
/** AST nodes related to error */
readonly nodes?: ReadonlyArray<ASTNode>;
/** Convert error to JSON format for responses */
toJSON(): GraphQLFormattedError;
}
interface GraphQLErrorOptions {
/** AST nodes where error occurred */
nodes?: ReadonlyArray<ASTNode>;
/** Source document */
source?: Source;
/** Character positions in source */
positions?: ReadonlyArray<number>;
/** Response path */
path?: ReadonlyArray<string | number>;
/** Original error to wrap */
originalError?: Error;
/** Additional error data */
extensions?: GraphQLErrorExtensions;
}
interface GraphQLErrorExtensions {
[key: string]: unknown;
}Usage Examples:
import { GraphQLError } from "graphql";
// Basic error
const error = new GraphQLError("Something went wrong");
// Error with location information
const errorWithLocation = new GraphQLError(
"Invalid field selection",
{
nodes: [fieldNode],
path: ["user", "invalidField"]
}
);
// Error with extensions
const errorWithExtensions = new GraphQLError(
"Authentication required",
{
extensions: {
code: "UNAUTHENTICATED",
statusCode: 401,
timestamp: new Date().toISOString()
}
}
);
// Error wrapping original error
const wrappedError = new GraphQLError(
"Database connection failed",
{
originalError: originalDbError,
extensions: { code: "DATABASE_ERROR" }
}
);Utility functions for creating specific types of GraphQL errors.
/**
* Create a syntax error for GraphQL parsing
* @param source - Source document
* @param position - Character position of error
* @param description - Error description
* @returns GraphQL syntax error
*/
function syntaxError(
source: Source,
position: number,
description: string
): GraphQLError;
/**
* Wrap an error with GraphQL location information
* @param originalError - Original error to wrap
* @param nodes - AST nodes where error occurred
* @param path - Response path where error occurred
* @returns GraphQL error with location info
*/
function locatedError(
originalError: Error | GraphQLError,
nodes?: ReadonlyArray<ASTNode>,
path?: ReadonlyArray<string | number>
): GraphQLError;Usage Examples:
import { syntaxError, locatedError, Source } from "graphql";
// Create syntax error during parsing
const source = new Source("{ user { name }"); // missing closing brace
const parseError = syntaxError(source, 13, "Expected '}'");
// Wrap resolver error with location
const resolverError = new Error("User not found");
const locatedResolverError = locatedError(
resolverError,
[fieldNode],
["user"]
);
// Use in field resolver
const userResolver = async (parent, args, context, info) => {
try {
return await fetchUser(args.id);
} catch (error) {
throw locatedError(error, info.fieldNodes, info.path);
}
};Functions for formatting errors for different output contexts.
/**
* Format GraphQL error for JSON response
* @param error - GraphQL error to format
* @returns Formatted error object
*/
function formatError(error: GraphQLError): GraphQLFormattedError;
/**
* Print GraphQL error with source context
* @param error - GraphQL error to print
* @returns Human-readable error string with source context
*/
function printError(error: GraphQLError): string;
interface GraphQLFormattedError {
/** Error message */
readonly message: string;
/** Source locations */
readonly locations?: ReadonlyArray<SourceLocation>;
/** Response path */
readonly path?: ReadonlyArray<string | number>;
/** Additional error data */
readonly extensions?: GraphQLFormattedErrorExtensions;
}
interface GraphQLFormattedErrorExtensions {
[key: string]: unknown;
}
interface SourceLocation {
readonly line: number;
readonly column: number;
}Usage Examples:
import { formatError, printError, GraphQLError } from "graphql";
const error = new GraphQLError(
"Cannot query field 'invalidField' on type 'User'",
{
nodes: [fieldNode],
path: ["user", "invalidField"],
extensions: { code: "INVALID_FIELD" }
}
);
// Format for JSON response
const formattedError = formatError(error);
console.log(JSON.stringify(formattedError, null, 2));
/*
{
"message": "Cannot query field 'invalidField' on type 'User'",
"locations": [{ "line": 3, "column": 5 }],
"path": ["user", "invalidField"],
"extensions": { "code": "INVALID_FIELD" }
}
*/
// Print with source context for debugging
const errorString = printError(error);
console.log(errorString);
/*
Cannot query field 'invalidField' on type 'User'
GraphQL request:3:5
2 | user {
3 | invalidField
| ^
4 | }
*/
// Format multiple errors from execution result
const result = await execute({ schema, document, rootValue });
if (result.errors) {
const formattedErrors = result.errors.map(formatError);
return { data: result.data, errors: formattedErrors };
}Best practices for error handling in field resolvers.
Usage Examples:
import { GraphQLError, locatedError } from "graphql";
// Field resolver with proper error handling
const userResolver = async (parent, args, context, info) => {
try {
// Check authentication
if (!context.user) {
throw new GraphQLError("Authentication required", {
extensions: { code: "UNAUTHENTICATED" }
});
}
// Check authorization
if (!context.user.canAccessUser(args.id)) {
throw new GraphQLError("Insufficient permissions", {
extensions: { code: "FORBIDDEN" }
});
}
// Fetch user data
const user = await context.dataSources.users.findById(args.id);
if (!user) {
throw new GraphQLError(`User not found: ${args.id}`, {
extensions: { code: "NOT_FOUND" }
});
}
return user;
} catch (error) {
// Wrap non-GraphQL errors
if (!(error instanceof GraphQLError)) {
throw locatedError(error, info.fieldNodes, info.path);
}
throw error;
}
};
// Custom error classes for different scenarios
class AuthenticationError extends GraphQLError {
constructor(message = "Authentication required") {
super(message, {
extensions: { code: "UNAUTHENTICATED" }
});
}
}
class ValidationError extends GraphQLError {
constructor(message: string, field?: string) {
super(message, {
extensions: {
code: "VALIDATION_ERROR",
field
}
});
}
}
// Usage in resolvers
const createUserResolver = async (parent, args, context) => {
if (!context.user) {
throw new AuthenticationError();
}
if (!args.input.email) {
throw new ValidationError("Email is required", "email");
}
if (!isValidEmail(args.input.email)) {
throw new ValidationError("Invalid email format", "email");
}
try {
return await context.dataSources.users.create(args.input);
} catch (dbError) {
if (dbError.code === "DUPLICATE_EMAIL") {
throw new ValidationError("Email already exists", "email");
}
throw new GraphQLError("Failed to create user", {
originalError: dbError,
extensions: { code: "DATABASE_ERROR" }
});
}
};How errors are handled during GraphQL execution.
Usage Examples:
import { execute, formatError } from "graphql";
// Execute with error handling
const executeWithErrorHandling = async (args) => {
try {
const result = await execute(args);
// Handle execution errors
if (result.errors && result.errors.length > 0) {
// Log errors for debugging
result.errors.forEach(error => {
console.error("GraphQL Error:", printError(error));
// Log original error if available
if (error.originalError) {
console.error("Original Error:", error.originalError);
}
});
// Format errors for response
return {
data: result.data,
errors: result.errors.map(formatError)
};
}
return { data: result.data };
} catch (error) {
// Handle unexpected errors
console.error("Unexpected execution error:", error);
return {
data: null,
errors: [formatError(new GraphQLError("Internal server error"))]
};
}
};
// Custom error formatter
const customFormatError = (error: GraphQLError) => {
// Don't expose internal errors to clients
if (error.originalError && error.extensions?.code !== "VALIDATION_ERROR") {
return formatError(new GraphQLError("Internal server error"));
}
// Format validation errors with more detail
if (error.extensions?.code === "VALIDATION_ERROR") {
return {
...formatError(error),
extensions: {
...error.extensions,
timestamp: new Date().toISOString()
}
};
}
return formatError(error);
};
// Use custom formatter
const result = await execute(args);
if (result.errors) {
return {
data: result.data,
errors: result.errors.map(customFormatError)
};
}Common patterns for error extensions and error codes.
// Common error extension patterns
interface ErrorExtensions {
/** Error code for programmatic handling */
code?: string;
/** HTTP status code */
statusCode?: number;
/** Timestamp when error occurred */
timestamp?: string;
/** Additional context data */
context?: Record<string, unknown>;
/** Stack trace (development only) */
stacktrace?: string[];
}
// Standard error codes
enum ErrorCode {
GRAPHQL_PARSE_FAILED = "GRAPHQL_PARSE_FAILED",
GRAPHQL_VALIDATION_FAILED = "GRAPHQL_VALIDATION_FAILED",
BAD_USER_INPUT = "BAD_USER_INPUT",
UNAUTHENTICATED = "UNAUTHENTICATED",
FORBIDDEN = "FORBIDDEN",
NOT_FOUND = "NOT_FOUND",
INTERNAL_ERROR = "INTERNAL_ERROR"
}Usage Examples:
// Structured error with comprehensive extensions
const createStructuredError = (
message: string,
code: string,
statusCode?: number,
additionalData?: Record<string, unknown>
) => {
return new GraphQLError(message, {
extensions: {
code,
statusCode,
timestamp: new Date().toISOString(),
...additionalData
}
});
};
// Usage examples
const authError = createStructuredError(
"Authentication required",
"UNAUTHENTICATED",
401
);
const validationError = createStructuredError(
"Invalid input data",
"BAD_USER_INPUT",
400,
{
field: "email",
rejectedValue: "invalid-email"
}
);