1

At the moment, I'm throwing RuntimeException's to return GraphQL validation errors. It works surprisingly well, with the exception that it throws horrible errors with large stack traces in my logs.

Here you can see I'm checking the submitted new user registration mutation to be sure the passwords match one another and the email address isn't already in use.

What is the correct way to do this in GraphQL SPQR Spring Boot Starter.

@GraphQLMutation (name="register")
public User register(@GraphQLArgument(name="firstname") String firstname, @GraphQLArgument(name="lastname") String lastname, @GraphQLArgument(name="email") String email, @GraphQLArgument(name="msisdn") String msisdn, @GraphQLArgument(name="password") String password, @GraphQLArgument (name="confirmPassword") String confirmPassword) {
    if (userRepo.findByEmail(email) != null) {
        throw new RuntimeException("User already exists");
    }

    if (!password.equals(confirmPassword)) {
        throw new RuntimeException("Passwords do not match");
    }

    User newUser = new User();
    //...
    return userRepo.save(newUser);
}
2
  • I don't know if there is a conventional way to delegate the validation through annotations or something similar, but for input validation I would throw IllegalArgumentException instead of RuntimeException, you can use google Preconditions for those checks too Commented Jul 26, 2019 at 7:59
  • 1
    Do you want to reduce the size of stack trace ? Is this is what you intended.If yes then you can override the DataFetcherExceptionHandler and create custom handler and hook into the GraphQL. Commented Jul 26, 2019 at 9:56

1 Answer 1

7

It's unclear to me what you're asking... but I'll assume you want to customize what's getting logged.

For starters, I'd suggest a dedicated exception type like ValidationException, that you can catch and handle differently.

As for the logging, it's probably happening in grapqh-java as SPQR doesn't log anything by itself. By default graphql-java uses SimpleDataFetcherExceptionHandler which logs the exceptions caught during field resolution.

You now have a couple of options, you could register a ResolverInterceptor in SPQR that catches validation exceptions and logs what you want, and returns a DataFetcherResult with the error message for the user. Because no validation exceptions bubble up to graphql-java, DataFetcherExceptionHandler has nothing to do in this scenario.

It would look something like:

public class ValidationInterceptor implements ResolverInterceptor {

    @Override
    public Object aroundInvoke(InvocationContext context, Continuation continuation) throws Exception {
        try {
            return continuation.proceed(context);
        } catch (ValidationException e) {
            log.warning(e);
            return DataFetcherResult.newResult()
                    .error(GraphqlErrorBuilder
                            .newError(context.getResolutionEnvironment().dataFetchingEnvironment)
                            .message(e.getMessage()) //the message for the user
                            .build());
        }
    }
}

Look at the answer here for instructions on registering your custom interceptor with Spring Boot.

Another option would be to replace the DataFetcherExceptionHandler graphql-java uses. To do that you must construct GraphQL object yourself and register it as a bean.

@Bean
public GraphQL graphQL(GraphQLSchema schema) {
    GraphQL.Builder builder = GraphQL.newGraphQL(schema)
            .queryExecutionStrategy(new AsyncExecutionStrategy(customExceptionHandler))
            .mutationExecutionStrategy(new AsyncSerialExecutionStrategy(customExceptionHandler));
    return builder.build();
}

I also wouldn't be surprised if there's a Spring feature somewhere that can be used for exception handling on managed beans.

Sign up to request clarification or add additional context in comments.

6 Comments

In my resolver interceptor I am instead receiving a InvocationTargetException that is wrapping the exception that I want to catch, what gives?
@Unidan This is normal for exceptions during reflective invocation, but SPQR should be unwrapping those. What version are you on?
I'm on 0.10.0. FWIW interceptor is in Kotlin, but all resolvers, and their underlying data models, are in Java.
@Unidan That's bug, I'm afraid (just verified it myself). Will fix in the next release.
All right, thanks for the quick response. Absolutely loving the library, even if there's barely any documentation :D!
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.