GraphQL specification defines a clear format for the error
entry in the response.
According to the spec, it should like this (assuming JSON format is used):
"errors": [
{
"message": "Name for character with ID 1002 could not be fetched.",
"locations": [ { "line": 6, "column": 7 } ],
"path": [ "hero", "heroFriends", 1, "name" ]
"extensions": {/* You can place data in any format here */}
}
]
So you won't find a GraphQL implementation that allows you to extend it and return some like this in the GraphQL execution result, for example:
"errors": [
{
"errorMessage": "Name for character with ID 1002 could not be fetched.",
"errorCode": 404
}
]
However, the spec lets you add data in whatever format in the extension
entry. So you could create a custom Exception on the server side and end up with a response that looks like this in JSON:
"errors": [
{
"message": "Name for character with ID 1002 could not be fetched.",
"locations": [ { "line": 6, "column": 7 } ],
"path": [ "hero", "heroFriends", 1, "name" ]
"extensions": {
"errorMessage": "Name for character with ID 1002 could not be fetched.",
"errorCode": 404
}
}
]
It's quite easy to implement this on GraphQL Java, as described in the docs. You can create a custom exception that overrides the getExtensions
method and create a map inside the implementation that will then be used to build the content of extensions
:
public class CustomException extends RuntimeException implements GraphQLError {
private final int errorCode;
public CustomException(int errorCode, String errorMessage) {
super(errorMessage);
this.errorCode = errorCode;
}
@Override
public Map<String, Object> getExtensions() {
Map<String, Object> customAttributes = new LinkedHashMap<>();
customAttributes.put("errorCode", this.errorCode);
customAttributes.put("errorMessage", this.getMessage());
return customAttributes;
}
@Override
public List<SourceLocation> getLocations() {
return null;
}
@Override
public ErrorType getErrorType() {
return null;
}
}
then you can throw the exception passing in the code and message from inside your data fetchers:
throw new CustomException(400, "A custom error message");
Now, there is another way to tackle this.
Assuming you are working on a Web application, you can return errors (and data, for that matter) in whatever format that you want. Although that is a bit awkward in my opinion. GraphQL clients, like Apollo, adhere to the spec, so why would you want to return a response on any other format? But anyway, there are lots of different requirements out there.
Once you get a hold of an ExecutionResult
, you can create a map or object in whatever format you want, serialise that as JSON and return this over HTTP.
Map<String, Object> result = new HashMap<>();
result.put("data", executionResult.getData());
List<Map<String, Object>> errors = executionResult.getErrors()
.stream()
.map(error -> {
Map<String, Object> errorMap = new HashMap<>();
errorMap.put("errorMessage", error.getMessage());
errorMap.put("errorCode", 404); // get the code somehow from the error object
return errorMap;
})
.collect(toList());
result.put("errors", errors);
// Serialize "result" and return that.
But again, having a response that doesn't comply with the spec doesn't make sense in most of the cases.