Error handling is a critical aspect of building robust and reliable GraphQL APIs. As developers, we must ensure that error responses are informative, consistent, and easy to handle on the client side.
In this article, we will explore advanced error-handling techniques in GraphQL. By leveraging these techniques, you can enhance user experience, provide meaningful error messages, and improve debugging.
We will use the GraphQLError class of GraphQL to play with errors. You can provide informative and consistent error responses by customizing error messages, implementing validation rules, and centralizing error-handling logic. Additionally, advanced error-handling techniques allow you to catch and handle errors in nested resolvers and test different error scenarios.
The full implementation of GraphQL APIs including error handling, unit tests, and API documentation is available at github.
Develop a growth mindset that sees failures as opportunities for growth with Justly today.
A GraphQLError describes an Error found during the parse, validate, or execute phases of performing a GraphQL operation.
GraphQLError’s constructor contains the following fields.
message: string, // error message
nodes?: ReadonlyArray<ASTNode> | ASTNode | null, // nodes corresponding to error
source?: Maybe<Source>, // the first location of error
positions?: Maybe<ReadonlyArray<number>>, // array of character offsets which correspond to error
path?: Maybe<ReadonlyArray<string | number>>, // path where error occured
originalError?: Maybe<
Error & {
readonly extensions?: unknown;
}
>,
extensions?: Maybe<GraphQLErrorExtensions>, // metadata for the error
Using the above fields, we can create more readable and easy-to-debug errors in graphQL.
Let’s use these fields and learn how we can customize errors quickly.
Sometimes it's more meaningful to provide custom error messages and details than default ones from libraries. It provides more context for errors and makes debug process easy.
In GraphQL, instead of returning generic error messages, you can create custom error types and extensions to provide specific details about errors.
You can add a custom error message in GraphQLError
like the one below,
const CustomError = new GraphQLError('Your custom message here...');
GraphQLError
contains field extensions. You can add different metadata related to errors like date and time
, Ids
, status
or other fields
, which can be helpful to debug the code.
Here is an example,
// Throwing custom error with extensions
throw new GraphQLError(
'Your custom message here...',
null,
null,
null,
null,
null,
{ code: 'CUSTOM_ERROR', date: Date.now(), id: userId, status: 400}
);
Default errors provide many error types and different data for each error type. When we are working with large applications, we need to manage error types that are feasible to handle and easy to recognize.
We can create as many errors as per our requirement. Some examples are BadRequestError
, UnauthorizedError
and ServerError
.
We will create a base error interface using GraphQLError
which will be used to implement any error types.
// Base interface : HttpException.ts
import { GraphQLError } from "graphql";
class HttpException extends GraphQLError {
status: number;
message: string;
extensions: {};
constructor(status: number, message: string, code?: string) {
super(message);
this.message = message;
this.extensions = { code: code, status: status };
}
}
export default HttpException;
Now, we can create the required error interface like below,
// UnauthorizedException.ts
import HttpException from "./HttpException";
class UnauthorizedException extends HttpException {
constructor(message?: string, code?: string) {
super(
401,
message || "Not authorized",
"UNAUTHORIZED_ERROR"
);
}
}
export default UnauthorizedException;
We can use this like the below,
try {
// TODO: write your logic here
} catch (error: any) {
throw new UnauthorizedException("Your custom error message...");
}
GraphQL error format is too detailed and sometimes we need a few details only. We can create a custom error format at a centralized location, which will be used as a global format for the whole application errors in GraphQL.
By considering our error interface, we can customize the error format like below,
function customErrorFormat(error: any) => {
return {
message: error.message,
code: error.extensions.code || "INTERNAL_SERVER_ERROR",
status: error.extensions.status || 500,
};
},
// use like below in apolloserver or graphqlHTTP
new ApolloServer({
schema,
formatError: customErrorFormat
})
// or
graphqlHTTP({
schema,
formatError: customErrorFormat
})
Error bubbling and error handling is concepts to ensure that error has been handled correctly at each stage of the nested functions or resolvers.
When a resolver encounters an error, it can propagate that error up the resolver chain until it reaches a higher-level resolver or the root resolver. This allows for centralized error handling and consistent error responses.
Consider a scenario where we have a GraphQL schema with a User
type and a Post
type. Each post has an associated author. We’ll demonstrate the concept with the following code snippets:
type Query {
post(id: ID!): Post
}
type Post {
id: ID!
title: String!
author: User
}
type User {
id: ID!
name: String!
}
const resolvers = {
Query: {
post: (_, { id }) => {
const post = getPostById(id);
if (!post) {
throw new Error("Post not found.");
}
return post;
},
},
Post: {
author: (post) => {
const author = getUserById(post.authorId);
if (!author) {
throw new Error("Author not found.");
}
return author;
},
},
};
Now, let’s assume a GraphQL query requests a post with its author:
query {
post(id: "123") {
id
title
author {
id
name
}
}
}
If the requested post is not found, an error is thrown in the post
resolver. If the author of the post is not found, an error is thrown in the author
resolver. The errors bubble up to the post
resolver, which catches them and includes them in the response sent back to the client.
By implementing error bubbling and handling in nested resolvers, you ensure consistent error handling and better user experience by providing clear and meaningful error messages throughout the resolver chain.
This post shows you the basics of error handling in GraphQL using GraphQLError class. Using this, we can easily customize errors whether it's database, validations, or any other errors.
By following the above steps, it's easy to identify and debug the errors at the application level.
We’re Grateful to have you with us on this journey!
Your feedback and suggestions are highly appreciated.
Please reach us at Canopas Twitter handle @canopas_eng with your content or feedback. Your input enriches our content and fuels our motivation to create more valuable and informative articles for you.
Keep exploring for the best.
Get started today
Let's build the next
big thing!
Let's improve your business's digital strategy and implement robust mobile apps to achieve your business objectives. Schedule Your Free Consultation Now.
Get Free ConsultationGet started today
Let's build the next big thing!
Let's improve your business's digital strategy and implement robust mobile apps to achieve your business objectives. Schedule Your Free Consultation Now.
Get Free Consultation