smallrye-graphql: Add ability for users to add error code

Hello there,

I’m a new person to GraphQL but in the last few days I was experimenting with it within the Quarkus framework, and I’m liking it so far. When it comes to writing code sometimes error can occur and I like to specify the error via error codes or with reasons which are straightforward and can be consumed easily by the caller. In the official GraphQL specification there are a few section about errors and one of them is specifying how the programmers could define “custom” error codes in their responses. Take a look at here: https://spec.graphql.org/June2018/#example-fce18

So what I see there if the programmer would like to indicate any kind of custom behavior in the error response they just have to put this information into the extension section in a key-value format. Easy. The problem with that in the SmallRye implementation this kind of stuff is not working properly (yet).

An example: I have a GraphQL API like the following:

@GraphQLApi
public class PackageResource {

    @Query("getPackages")
    @Description("Returns all available packages")
    public List<Package> getPackages() {
        throw GraphqlErrorException.newErrorException()
                .message("Something went wrong")
                .extensions(Map.of("code","CUSTOM_ERROR_CODE"))
                .build();
    }

}

I found the GraphqlErrorException class and it looks fine, because it has an extensions variable in it and it can be populated, so far so good, btw this class is coming from the com.graphql-java:graphql-java:14.0 dependency right now. So when this exception is being thrown it will be handled by the io.smallrye.graphql.execution.error.ExceptionHandler where it will be wrapped as a io.smallrye.graphql.execution.error.GraphQLExceptionWhileDataFetching exception which calls it’s super class constructor graphql.ExceptionWhileDataFetching and it sets any kind of incoming extensions in it.

public ExceptionWhileDataFetching(ExecutionPath path, Throwable exception, SourceLocation sourceLocation) {
        this.path = assertNotNull(path).toList();
        this.exception = assertNotNull(exception);
        this.locations = Collections.singletonList(sourceLocation);
        this.extensions = mkExtensions(exception);
        this.message = mkMessage(path, exception);
    }

At this point if a newly created exception is containing extra extensions then it will be populated there. The problems comes when this exception is being serialized by the io.smallrye.graphql.execution.error.ExecutionErrorsService#toJsonError method. It basically “replaces” or just simply ignores the content of the extensions field. io.smallrye.graphql.execution.error.ExecutionErrorsService#getDataFetchingExtensions

    private Optional<JsonObject> getDataFetchingExtensions(ExceptionWhileDataFetching error) {
        Throwable exception = error.getException();

        JsonObjectBuilder objectBuilder = jsonBuilderFactory.createObjectBuilder();

        addKeyValue(objectBuilder, EXCEPTION, exception.getClass().getName());
        addKeyValue(objectBuilder, CLASSIFICATION, error.getErrorType().toString());

        return Optional.of(objectBuilder.build());
    }

So my question is the following, is there any way to populate the response with the values in the extensions field within the ExceptionWhileDataFetching exception? If there is no specific reason for ignoring the custom values then I think a change could be made to the getDataFetchingExtensions method, if there is a specific reason for ignoring them, then please ignore this issue ticket 😃

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 15 (12 by maintainers)

Commits related to this issue

Most upvoted comments

This is now partially fix, I create another ticket for the outstanding (see #299)

I’ll work on supporting @GraphQlErrorCode now.

Awesome idea! I would like to go even a step further: there would be a big benefit, if there was a standard extension field for error codes. If code is the name of that extension commonly used in the GraphQL community, we should make it as easy as by any means possible to set in MP GraphQL:

class EntityNotFoundException extends RuntimeException {
}

The code could be derived directly from the simple class name!

If that is not what we want, we could specify the code explicitly:

@GraphQlErrorCode("ENTITY_NOT_FOUND")
class CustomException extends RuntimeException {
}

… or on a method if it needs to be dynamic:

class CustomException extends RuntimeException {
    @GraphQlErrorCode
    public String errorCode() {
        return "ENTITY_NOT_FOUND";
    }
}

… or on a field:

class CustomException extends RuntimeException {
    @GraphQlErrorCode
    private final String errorCode;

    // constructor
}

Any type could be used (esp. an enum), and it would be mapped to a string. I think it would only be extra work for clients to sometimes have other types, e.g. a numeric 404 in some cases… let’s stick to string.

On the client side, it would be ideal if the error code would be mapped back to an exception, but there are a number of open questions. We should discuss this in #167.

For REST services there’s a standard rfc-7807 that uses type instead of code, but it means the same thing: a permanent code to identify the type of error. They suggest to use an URI for the error type, so we could use a URL to the documentation of the code. But that’s RESTful thinking 😉 It’s actually not easy to have absolutely stable URLs, so it may be better to have a separate URL extension field for the documentation URL; it’s a separate concern after all.

There are other very useful things specified in rfc-7807 that we could use as well, but the code is by far the most important one. Let’s start with that and continue from there.

Hi @nandorholozsnyak . Thanks for the detailed issue ! So I think there is two issues here:

  1. We should not ignore the extensions if it’s already populated when using a graphql-java exception (or any other exception with that field maybe ?) is used. There is no reason for ignoring them.
  2. We need a way to allow developers to add to the extension map (without having to use a exception class that contain an extension field.

Both of these issues should be handled in the spec, but we can fix it here in the mean time.