Techdozo
  • Microservices
    • All
    • GraphQL
    • gRPC
    • Spring Boot
    GraphQL Error Handling

    GraphQL Error Handling

    Pages

    Spring for GraphQL: Pagination with Code Example

    Spring for GraphQL: Interfaces and Unions

    gRPC Bidirectional Streaming with Code Example

    gRPC Bidirectional Streaming with Code Example

    gRPC Client Streaming

    gRPC Client Streaming

    Distributed transaction in microservices using Saga

    Distributed Transactions in Microservices: implementing Saga with Temporal

  • Spring Boot
    Pages

    Spring for GraphQL: Pagination with Code Example

    Spring Boot GraphQL service

    Getting started with Spring Boot GraphQL service

    Deploying a RESTful Spring Boot Microservice on Kubernetes

    Deploying a RESTful Spring Boot Microservice on Kubernetes

    RESTful Microservices with Spring Boot and Kubernetes

    RESTful Microservices with Spring Boot and Kubernetes

    RESTful API Gateway with gRPC

    RESTful API Gateway with gRPC

  • gRPC
    gRPC Bidirectional Streaming with Code Example

    gRPC Bidirectional Streaming with Code Example

    gRPC Client Streaming

    gRPC Client Streaming

    gRPC Interceptor: unary interceptor with code example

    gRPC Interceptor: unary interceptor with code example

    gRPC: synchronous and asynchronous Server streaming RPC

    gRPC: synchronous and asynchronous Server streaming RPC

    Photo by Ramón Salinero on Unsplash

    gRPC: synchronous and asynchronous unary RPC in Java

    Microservices inter-process communication using gRPC

    gRPC for microservices communication

  • GraphQL
    GraphQL Error Handling

    GraphQL Error Handling

    Pages

    Spring for GraphQL: Pagination with Code Example

    Spring for GraphQL: Interfaces and Unions

    GraphQL Directive

    GraphQL Directive

    Spring for GraphQL mutation

    Spring for GraphQL: Mutation

    Spring for GraphQL: How to solve the N+1 Problem?

    Spring for GraphQL: How to solve the N+1 Problem?

    Spring GraphQL with @Controller, @SchemaMapping and @QueryMapping

    Spring for GraphQL : @SchemaMapping and @QueryMapping

    Spring Boot GraphQL service

    Getting started with Spring Boot GraphQL service

  • Kubernetes
    Deploying a RESTful Spring Boot Microservice on Kubernetes

    Deploying a RESTful Spring Boot Microservice on Kubernetes

    Components of Kubernetes Architecture

    Components of Kubernetes Architecture

    Helm Chart: quick start your app deployment on Kubernetes

    Helm Chart: quick start your app deployment on Kubernetes

    gRPC load balancing on Kubernetes (using Headless Service)

    gRPC load balancing on Kubernetes (using Headless Service)

    Getting started with Kind: quick start a multi-node local Kubernetes cluster

    Getting started with Kind: quick start a multi-node local Kubernetes cluster

    Getting started with Minikube: deploying application on local Kubernetes cluster

    Getting started with Minikube: deploying application on local Kubernetes cluster

  • Java
    Java Streams: Stream Operation with Examples

    Java Streams: Stream Operation with Examples

    Java Streams: stream creation with examples

    Java Streams: stream creation with examples

    Garbage Collection

    Super Fast Garbage Collectors in Java

    Calculus

    Functional Programming in Java

No Result
View All Result
  • Login
  • Microservices
    • All
    • GraphQL
    • gRPC
    • Spring Boot
    GraphQL Error Handling

    GraphQL Error Handling

    Pages

    Spring for GraphQL: Pagination with Code Example

    Spring for GraphQL: Interfaces and Unions

    gRPC Bidirectional Streaming with Code Example

    gRPC Bidirectional Streaming with Code Example

    gRPC Client Streaming

    gRPC Client Streaming

    Distributed transaction in microservices using Saga

    Distributed Transactions in Microservices: implementing Saga with Temporal

  • Spring Boot
    Pages

    Spring for GraphQL: Pagination with Code Example

    Spring Boot GraphQL service

    Getting started with Spring Boot GraphQL service

    Deploying a RESTful Spring Boot Microservice on Kubernetes

    Deploying a RESTful Spring Boot Microservice on Kubernetes

    RESTful Microservices with Spring Boot and Kubernetes

    RESTful Microservices with Spring Boot and Kubernetes

    RESTful API Gateway with gRPC

    RESTful API Gateway with gRPC

  • gRPC
    gRPC Bidirectional Streaming with Code Example

    gRPC Bidirectional Streaming with Code Example

    gRPC Client Streaming

    gRPC Client Streaming

    gRPC Interceptor: unary interceptor with code example

    gRPC Interceptor: unary interceptor with code example

    gRPC: synchronous and asynchronous Server streaming RPC

    gRPC: synchronous and asynchronous Server streaming RPC

    Photo by Ramón Salinero on Unsplash

    gRPC: synchronous and asynchronous unary RPC in Java

    Microservices inter-process communication using gRPC

    gRPC for microservices communication

  • GraphQL
    GraphQL Error Handling

    GraphQL Error Handling

    Pages

    Spring for GraphQL: Pagination with Code Example

    Spring for GraphQL: Interfaces and Unions

    GraphQL Directive

    GraphQL Directive

    Spring for GraphQL mutation

    Spring for GraphQL: Mutation

    Spring for GraphQL: How to solve the N+1 Problem?

    Spring for GraphQL: How to solve the N+1 Problem?

    Spring GraphQL with @Controller, @SchemaMapping and @QueryMapping

    Spring for GraphQL : @SchemaMapping and @QueryMapping

    Spring Boot GraphQL service

    Getting started with Spring Boot GraphQL service

  • Kubernetes
    Deploying a RESTful Spring Boot Microservice on Kubernetes

    Deploying a RESTful Spring Boot Microservice on Kubernetes

    Components of Kubernetes Architecture

    Components of Kubernetes Architecture

    Helm Chart: quick start your app deployment on Kubernetes

    Helm Chart: quick start your app deployment on Kubernetes

    gRPC load balancing on Kubernetes (using Headless Service)

    gRPC load balancing on Kubernetes (using Headless Service)

    Getting started with Kind: quick start a multi-node local Kubernetes cluster

    Getting started with Kind: quick start a multi-node local Kubernetes cluster

    Getting started with Minikube: deploying application on local Kubernetes cluster

    Getting started with Minikube: deploying application on local Kubernetes cluster

  • Java
    Java Streams: Stream Operation with Examples

    Java Streams: Stream Operation with Examples

    Java Streams: stream creation with examples

    Java Streams: stream creation with examples

    Garbage Collection

    Super Fast Garbage Collectors in Java

    Calculus

    Functional Programming in Java

No Result
View All Result
Techdozo
No Result
View All Result
Home Microservices GraphQL

GraphQL Error Handling

Pankaj by Pankaj
August 9, 2023
in GraphQL, Microservices
Reading Time: 13 mins read
0
A A
0
GraphQL Error Handling
ADVERTISEMENT

GraphQL APIs differ significantly from REST APIs when it comes to error handling. While partial responses are not possible in REST APIs, GraphQL APIs are designed to allow partial responses in the event of an error condition. This presents some interesting challenges and design considerations that developers and architects should know.

In this article, we will explore the error-handling capabilities of GraphQL. We will examine the GraphQL response structure and how it helps us effectively convey errors. Additionally, we will examine the HTTP status codes returned by the GraphQL service, mainly in the context of Spring for GraphQL. Finally, we will see code examples of handling errors by customizing the errors returned by Spring for GraphQL.

ADVERTISEMENT

In this article

  • GraphQL Response Format
  • HTTP Status Codes
  • Partial Response
  • GraphQL Errors
    • Request Errors
    • Field Errors
  • Error Classification
  • Custom Error Handling
  • Code
  • Summary

If you’re interested in learning more about GraphQL, be sure to check out our other posts.

  • Spring for GraphQL : @SchemaMapping and @QueryMapping
  • Spring for GraphQL: How to solve the N+1 Problem?
  • Spring for GraphQL: Mutation
  • GraphQL Directive
  • Spring for GraphQL: Pagination with Code Example
  • Getting started with Spring Boot GraphQL service

GraphQL Response Format

When a GraphQL API is queried, the response is a JSON object that follows a specific format. This format includes three main components: data, errors, and extensions. The “data” contains the requested data, while the “errors” contains a list of any errors encountered during the query. The “extensions” is an optional field that can include additional information about the query, such as performance metrics, tracing, etc.

{
  "data": <data>,
  "errors": <list of GraphQL errors>,
  "extensions": <map of extensions>
}

HTTP Status Codes

The GraphQL specification does not require a specific transport protocol for communication between the client and server. However, HTTP is the most commonly used transport protocol in practice, and JSON is the most popular serialization choice for GraphQL APIs. Additionally, every GraphQL request is an HTTP POST request.

In Spring for GraphQL, the transport protocols are managed by Spring (either Spring MVC or Webflux). The underlying GraphQL Java library does not impose any specific transport protocol.

During normal execution, the server always returns an HTTP status code of 200 OK, even in the case of an error. However, if there are errors, the response body contains an “errors” field in JSON response body. This is an important distinction from a REST API, as it allows the server to provide a partial response with errors, explaining why some data could not be retrieved.

In certain cases, a GraphQL request may be rejected before being processed by the GraphQL engine. In these instances, the HTTP status code can indicate the issue with the request. For instance, a 401 Unauthorized code is returned for authentication failures, while a 400 Bad Request is returned for invalid GraphQL requests.

ADVERTISEMENT

Partial Response

Let’s understand how errors are returned by the GraphQL service using the following GraphQL schema as a reference.

type Query {
    bookById(id : ID) : Book
}

type Book {
    id : ID
    name : String
    author: String
    price: Float
    ratings: [Rating]
}

type Rating {
    id: ID
    rating: Int
    comment: String
    user: String
}

A client can request a book by its ID as follows:

query GetBookByID {
  bookById(id: 1) {
    id
    name
    author
    ratings {
      id
      rating
      comment
    }
  }
}

Under normal circumstances, the server’s response can look like:

{
  "data": {
    "bookById": {
      "id": "1",
      "name": "Zero to One",
      "author": "Peter Thiel",
      "ratings": [
        {
          "id": "1",
          "rating": 5,
          "comment": "The 4 minutes that will help you decide if this book is for you"
        },
        {
          "id": "2",
          "rating": 3,
          "comment": "Is Peter Thiel the next robber baron?"
        },
        {
          "id": "3",
          "rating": 3,
          "comment": "Simple-minded. Is it satire? Poorly-reasoned? Historically-ignorant? Afraid of competition? IDK"
        }
      ]
    }
  }
}

Above is an HTTP POST request, and the server returned a status code of 200.

If we simulate an error condition, such as throwing a RuntimeException, the server will return an error.

@QueryMapping
  public Book bookById(@Argument Integer id) {
    throw new RuntimeException("Something went wrong");
  }

The response returned by the server can be in the following format:

{
  "errors": [
    {
      "message": "INTERNAL_ERROR for 0b4ea8f6-1",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "bookById"
      ],
      "extensions": {
        "classification": "INTERNAL_ERROR"
      }
    }
  ],
  "data": {
    "bookById": null
  }
}

Notice that data and errors are present in the response, even though the data is null.

This becomes more interesting when the child data fetcher throws an exception.

@QueryMapping
  public Book bookById(@Argument Integer id) {
    return bookCatalogService.bookById(id);
  }

  @SchemaMapping
  public List<Rating> ratings(Book book) {
    throw new RuntimeException("Something went wrong");
    //    return bookCatalogService.ratings(book);
  }

The response returned by the server in this case is:

{
  "errors": [
    {
      "message": "INTERNAL_ERROR for bdb240fb-4",
      "locations": [
        {
          "line": 6,
          "column": 5
        }
      ],
      "path": [
        "bookById",
        "ratings"
      ],
      "extensions": {
        "classification": "INTERNAL_ERROR"
      }
    }
  ],
  "data": {
    "bookById": {
      "id": "1",
      "name": "Zero to One",
      "author": "Peter Thiel",
      "ratings": null
    }
  }
}

As you can see, the server returns a partial response, with the ratings field as null and not-so-useful error messages.

In GraphQL, every “errors” field must contain a “message” field that describes the error. The “locations” field is linked to the GraphQL request document to make it easier for developers to find the cause of the error.

The GraphQL specification also allows for an optional key extensions, which can be a map of additional data. The GraphQL specification doesn’t enforce any restrictions on the map’s content. In the preceding example, the error classification is INTERNAL_ERROR.

GraphQL Errors

The errors in GraphQL can be classified into two categories: request errors and field errors.

Request Errors

The request error occurs when there is an issue with the request itself, such as invalid GraphQL syntax.

In this case, the GraphQL response will contain the errors key but not the data key.

Field Errors

A field error occurs when an error occurs during the execution of a particular field, resulting in a partial response. This error may occur due to an internal error during the execution of DataFetcher.

Field errors are typically the fault of the GraphQL service.

Due to partial response, a client cannot rely solely on the null value. It must always inspect the errors field to determine the error condition.

Error Classification

GraphQL Java provides classifications for commonly occurring errors in the class ErrorType.

ClassificationDescription
InvalidSyntaxinvalid GraphQL syntax
ValidationErrorRequest error due to invalid request
DataFetchingExceptionField error raised during data fetching
NullValueInNonNullableFieldField error when a field is defined as non-null in the schema
returns a null value
OperationNotSupportedRequest error if a request attempts to perform an operation not defined in the schema
ExecutionAbortedIndicates that the current execution should be aborted

Spring for GraphQL adds a few more error classifications, such as:

  • BAD_REQUEST
  • UNAUTHORIZED
  • FORBIDDEN
  • NOT_FOUND
  • INTERNAL_ERROR

If any exception is unresolved, then it will be classified by default as INTERNAL_ERROR.

Custom Error Handling

Spring for GraphQL enables you to include custom error classifications if the provided classification is insufficient for your use case. Additionally, you can add custom metadata, such as the error code, in the extensions field. This provides a standardized way of communicating problems between server and client applications.

Let’s try to understand this through an example!

A popular approach to handling application errors is throwing a domain-specific exception and letting the appropriate layer handle it. For example, when asked for a particular ID, the repository implementation may throw ResourceNotFoundException if the requested ID doesn’t exist.

public Book findBookById(Integer bookId) {
    var book = bookStorage.get(bookId);
    if (book == null) {
      throw new ResourceNotFoundException("Book not found", Map.of("ID", String.valueOf(bookId)));
    }
    return book;
  }

The error response returned to the client in this case is:

{
  "errors": [
    {
      "message": "INTERNAL_ERROR for dd08cb12-24ff-0856-2c04-2e342b0ab19e",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "bookById"
      ],
      "extensions": {
        "classification": "INTERNAL_ERROR"
      }
    }
  ],
  "data": {
    "bookById": null
  }
}

In the above error response, Spring for GraphQL includes a generic message to prevent the leakage of implementation details to the client.

To customize the error response, we can implement an exception resolver as follows:

@Component
public class CustomErrorMessageResolver extends DataFetcherExceptionResolverAdapter {
  @Override
  protected GraphQLError resolveToSingleError(Throwable ex, DataFetchingEnvironment env) {
    return GraphqlErrorBuilder.newError(env)
        .errorType(ErrorType.INTERNAL_ERROR) 
        .message("Not Found") 
        .build();
  }
}

In this code, we are extending DataFetcherExceptionResolverAdapter and overriding the resolveToSingleError method in the CustomErrorMessageResolver class. Then, we build a new error using GraphqlErrorBuilder and add the error type and message details.

The server returns the following response:

{
  "errors": [
    {
      "message": "Not Found",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "bookById"
      ],
      "extensions": {
        "classification": "NOT_FOUND"
      }
    }
  ],
  "data": {
    "bookById": null
  }
}

This is much better. However, what if you want to include custom classification and error codes in the response?

To achieve this, you can create custom error types and include error codes in the extensions field like this:

protected GraphQLError resolveToSingleError(Throwable ex, DataFetchingEnvironment env) {

  if (ex instanceof ResourceNotFoundException resourceNotFoundException) {
    var errorCode = resourceNotFoundException.getErrorCode();
    var message = resourceNotFoundException.getMessage();
    return GraphqlErrorBuilder.newError(env)
        .errorType(CustomErrorType.RESOURCE_NOT_FOUND)
        .message(message)
        .extensions(Map.of("errorCode", errorCode.getShortCode()))
        .build();
  }
  return null;
}

The syntax ex instanceof ResourceNotFoundException resourceNotFoundExceptionis Pattern Matching for instanceof

Where CustomErrorType is defined as:

public enum CustomErrorType implements ErrorClassification {
  RESOURCE_NOT_FOUND,
  INVALID_OPERATION;
}

The server’s response in this case is:

{
  "errors": [
    {
      "message": "Book not found",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "bookById"
      ],
      "extensions": {
        "errorCode": "ResourceNotFound",
        "classification": "RESOURCE_NOT_FOUND"
      }
    }
  ],
  "data": {
    "bookById": null
  }
}

Customizing error responses allows you to establish a standard contract with the client application. This hides implementation details by returning useful error messages and codes.

Code

The working code example for this article is available on GitHub. To run the example, clone the repository and import graphql-spring-error as a Gradle project in your favorite IDE.

You can run the application from your IDE, which will start the GraphQL server on port 8080. To test the application, you can use GraphiQL.

Summary

The response of a GraphQL API includes three main fields: “data”, “errors”, and “extensions”. The “data” field contains the requested data, while the “errors” field contains a list of any errors encountered during the query. The optional “extensions” field can include additional information about the query.

HTTP status codes indicate issues with the request, and partial responses may be returned with errors to explain why certain data could not be retrieved. In GraphQL, errors can fall under two categories: request errors or field errors. Implementing custom error handling is recommended to provide clients with uniform error messages and codes.

Tags: graphql
Previous Post

Spring for GraphQL: Pagination with Code Example

Pankaj

Pankaj

Software Architect @ Schlumberger ``` Cloud | Microservices | Programming | Kubernetes | Architecture | Machine Learning | Java | Python ```

Related Posts

Pages
GraphQL

Spring for GraphQL: Pagination with Code Example

July 26, 2023
GraphQL

Spring for GraphQL: Interfaces and Unions

May 17, 2023
gRPC Bidirectional Streaming with Code Example
gRPC

gRPC Bidirectional Streaming with Code Example

February 17, 2023
gRPC Client Streaming
gRPC

gRPC Client Streaming

January 20, 2023

Recent Articles

GraphQL Error Handling

GraphQL Error Handling

August 9, 2023
Pages

Spring for GraphQL: Pagination with Code Example

July 26, 2023

Spring for GraphQL: Interfaces and Unions

May 17, 2023
gRPC Bidirectional Streaming with Code Example

gRPC Bidirectional Streaming with Code Example

February 17, 2023
  • Trending
  • Comments
  • Latest
gRPC Bidirectional Streaming with Code Example

gRPC Bidirectional Streaming with Code Example

February 17, 2023
Deploying a RESTful Spring Boot Microservice on Kubernetes

Deploying a RESTful Spring Boot Microservice on Kubernetes

August 18, 2021
gRPC Interceptor: unary interceptor with code example

gRPC Interceptor: unary interceptor with code example

April 30, 2022
Temporal Workflow Orchestration

Workflow Orchestration with Temporal and Spring Boot

October 29, 2022
Calculus

Functional Programming in Java

0
Java Streams: stream creation with examples

Java Streams: stream creation with examples

0
Garbage Collection

Super Fast Garbage Collectors in Java

0
Java Streams: Stream Operation with Examples

Java Streams: Stream Operation with Examples

0
GraphQL Error Handling

GraphQL Error Handling

August 9, 2023
Pages

Spring for GraphQL: Pagination with Code Example

July 26, 2023

Spring for GraphQL: Interfaces and Unions

May 17, 2023
gRPC Bidirectional Streaming with Code Example

gRPC Bidirectional Streaming with Code Example

February 17, 2023
Facebook Twitter Pinterest

TECHDOZO

Simplifying modern tech stack!

Browse by Category

  • Bitesize
  • GraphQL
  • gRPC
  • Java
  • Kubernetes
  • Microservices
  • Spring Boot

Recent Articles

GraphQL Error Handling

GraphQL Error Handling

August 9, 2023
Pages

Spring for GraphQL: Pagination with Code Example

July 26, 2023

© 2023 Techdozo.

Welcome Back!

Sign In with Google
OR

Login to your account below

Forgotten Password?

Retrieve your password

Please enter your username or email address to reset your password.

Log In
No Result
View All Result
  • Home
  • gRPC
  • Kubernetes
  • Microservices
  • GraphQL

© 2023 Techdozo.