1
votes

I'm implementing query layer on database by using GraphQl and spring boot project to perform CRUD operation on sql database. In GraphQL schema i mentioned some fields to be mandatory and when those fields are not mentioned in query it is returning ValidationError error message in default format with 200 status code.

Error :

{
"data": null,
"errors": [
    {
         value=StringValue{value='1235'}}]}}]}' is missing required fields '[book_type]' @ 'create_book'",
        "locations": [
            {
                "line": 3,
                "column": 23,
                "sourceName": null
            }
        ],
        "description": "argument 'insert' with value value=StringValue{value='1235'}}]}}]}' is missing required fields '[book_type]'",
        "validationErrorType": "WrongType",
        "queryPath": [
            "create_book"
        ],
        "errorType": "ValidationError",
        "path": null,
        "extensions": null
    }
],
"dataPresent": false,
"extensions": null
}

And here is my code with layer architecture pattern

Controller :

@Autowired
private GraphQLServer graphQlServer;

@PostMapping("test")
public ResponseEntity<Object> graphQl(@RequestBody String body){
    ExecutionResult response = graphQlServer.execute(body);

    return ResponseEntity.ok(response);
}

Service :

@Service
public class GraphQLServer {

@Autowired
private GraphQL graphQl;

public ExecutionResult execute(String query) {
    return graphQl.execute(query);
   }

}

Config :

@Bean
public GraphQL loadSchema() throws IOException {

    File schemaFile = schemaResource.getFile();

    TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(schemaFile);
    RuntimeWiring wiring = buildRuntimeWiring();
    GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(typeRegistry, wiring);
    return GraphQL.newGraphQL(schema).build();
}

private RuntimeWiring buildRuntimeWiring() {

    return RuntimeWiring.newRuntimeWiring()
            .type("Mutation", mutationWiring -> mutationWiring.dataFetcher("create_book", bookDataFetcher))
            .build();

}

BookDataFetcher :

@Override
public Map<String, Object> get(DataFetchingEnvironment environment) {

    //return data from db by getting Map properties from environment

}

The above code is working as expected but my question here is How to customize the error message? In the error message i would like to mention the status 400 since it is bad request from client

1
Do you know which is the JSON object that has fields like data, errors, dataPresentand extensions? if you know it, I can help you @Deadpool - Jonathan JOhx

1 Answers

1
votes

First of all , you should call toSpecification() on ExecutionResult to make sure the response obeys the GraphQL Specification.

By default , there is only one ExecutionResult 's implementation provided by which is ExecutionResultImpl , so you can cast ExecutionResult to it in order to use its transform() to update its state.

ExecutionResultImpl internally contains all errors detected by the . All of them are in the subclass of GraphQLError which mean you have to cast it to the specific sub-class during customization.

In your case , the subclass is ValidationError and the codes look something like :

@PostMapping("test")
public ResponseEntity<Object> graphQl(@RequestBody String body){

    ExecutionResult response = graphQlServer.execute(body);

    ExecutionResultImpl responseImpl = (ExecutionResultImpl) response;

    List<GraphQLError> customizedErrors = Lists.newArrayList();
    for (GraphQLError gqlError : responseImpl.getErrors()) {
            //Do your error custmosation here....
            GraphQLError customizedError = gqlError;
            if (gqlError instanceof ValidationError) {
                ValidationError error = (ValidationError) gqlError;
                customizedError = new ValidationError(error.getValidationErrorType(), error.getLocations(),
                        "Customizing some error message blablabla....");
            }
            customizedErrors.add(customizedError);
    }

    Map<String, Object> specResponse = responseImpl.transform(b->b.errors(customizedErrors)).toSpecification();

    return ResponseEntity.ok(specResponse);
}