Custom Error Handling in GraphQL — with examples

Error handling is a critical aspect of building robust and reliable GraphQL APIs.
Jul 4 2023 · 4 min read

Introduction

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.


Sponsored

Develop a growth mindset that sees failures as opportunities for growth with Justly today.


GraphQLError

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.


Customizing Errors

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.

Customizing error messages

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...');

Customizing extensions

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}
);

Custom error interface

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 .

Example

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...");
}

Global Error Handling

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 in Nested Resolvers

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.


Conclusion

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.

That’s it for today. Your feedback and suggestions are highly appreciated.

Keep exploring for the best.


Similar articles


sumita-k image
Sumita Kevat
Sumita is an experienced software developer with 5+ years in web development. Proficient in front-end and back-end technologies for creating scalable and efficient web applications. Passionate about staying current with emerging technologies to deliver.


sumita-k image
Sumita Kevat
Sumita is an experienced software developer with 5+ years in web development. Proficient in front-end and back-end technologies for creating scalable and efficient web applications. Passionate about staying current with emerging technologies to deliver.

background-image

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 Consultation
footer
Subscribe Here!
Follow us on
2024 Canopas Software LLP. All rights reserved.