Initial discussion in #160.
Validating data prior to triggering a mutation is a common (and critical) part of any action. I think it could be beneficial to add a validation key on the MutationOptions object that represents a method with the following signature ({ variables }) => boolean.
View layer integrations could consume this and even add in more data to the method (like props for React) ({ variables, ownProps }) => boolean.
Prior to creating the mutation, this method would run (defaulting to a true stub?) and could return an error if invalid.
Thoughts?
I'm all for that. I think it'll help reduce friction and greatly improve the experience my mutation can be validated by any business logic requirements
I don't think boolean is a good return type for this. It should be a list of errors, akin to ValidationError (perhaps _exactly_ validation error): https://github.com/meteor/validation-error
We should also make this the same as argument validation errors that would be returned by the server, since as we all know client-side validation is not enough! Pinging @helfer to see if he has thoughts about how validation errors would be returned from the server.
But there are two types of validation I see happening. Client side validation for the mutation, executed before it is sent to the server and one with the server itself?
I was thinking that the boolean will be good for custom business logic that runs before the boolean is returned. This could be more pluggable. Just some thoughts though
Yeah my thought is that the client and server validation code will be very similar if not identical in many cases.
Can't validation errors on the server be returned from the resolver?
or is that the wrong place for them?
I like the way redux-form does it's validation which is to have a function like:
(values) => {
const errors = {};
if(values.category !== 'news') errors.category = "Category must be news";
return errors;
}
Where errors is a free form object allowing you to use errors from an ORM or client side validation library etc.
I am currently working on a production app and I need a way to show validation errors. What's the best route that I can take as of now?
+1 for the above question
Anything new on this?
Really wish there was guidance in the docs for this. The only thing I could figure out is throwing an exception from my resolver.
Another example would be returning errors from queries - e.g. When searching for a user using graphql and getting a 404 Not found from the server.
Is there anyone who can give guidance on this?
@ryannealmes I think throwing errors in resolvers is fine, you can then look at the array of errors and display those in the UI.
Another alternative is to use union types and either return mutation data or a validation error (without throwing). If someone has tried that, I'd be curious to hear what their experience was.
@helfer how would you send an array of errors as a payload back to the client? Returning data doesn't do anything. Throwing an exception is possible, but that only allows a single error to be returned to the client. There isn't anyway to actually get a list of errors.
@ryannealmes oh, I guess you're checking all the arguments inside a single resolver. In that case it might be easiest if you use a union type to return validation errors. Alternatively, you could use a custom formatError function on the server which you could use to pass through an array of validation error messages while only throwing a single validation error (you'd just have to subclass Error with your custom ValidationError).
@helfer cool thanks I will try go with that and see how it comes out :)
@abhiaiyer91 @jbaxleyiii any news on this? What approach are you using for mutation validation?
Union types are interesting I guess you would have maybe something like this?
scalar Error
type Post {
id: String!
title: String!
imageUrl: String
}
type InputPost {
title: String!
imageUrl: String
}
type Query {
posts: [Post]
count: Int!
}
union OutputPost = Post | Error
type Mutation {
submitPost(post: InputPost): OutputPost
}
The client code would need to check for the shape of the response to handle error?
What about the pattern described in this article?
https://medium.com/@tarkus/validation-and-user-errors-in-graphql-mutations-39ca79cd00bf
I just started using GraphQL/Apollo/React last week -- first time making a client-side app, so consider me ignorant of best practices -- but I applied the mutation { model, errors } pattern for my login/signup forms (only ones I've done so far) and it seems to be working alright.
They're both nullable, so just return one or the other - no need for funky union types. (I don't think there's an XOR expression in GraphQL, so you won't be able to guarantee one and only one field is returned, but I don't personally care.)
I don't think this should live in Apollo Client core so I'll close the issue here. I think it could be easily implemented in react-apollo if desired for example.
Most helpful comment
Really wish there was guidance in the docs for this. The only thing I could figure out is throwing an exception from my resolver.
Another example would be returning errors from queries - e.g. When searching for a user using graphql and getting a 404 Not found from the server.
Is there anyone who can give guidance on this?