-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Validations for multiple fields in Apollo Server 2.0 #1166
Comments
@vespertilian This could be possible with a GraphQL extension that takes a combine error inside of the GraphQL response and splits it up multiple errors in the errors array. The current formatError function is created with this in mind
formatError .
We're currently documenting the extensions and this seems like a great use case! |
Thank's for getting back to me. What do you mean by a masking error? I am not sure I fully grasp your solution yet, but I am keen to try this out as soon as you document it, and as long as I can throw multiple validation errors for inputs and ideally have them look similar to the inbuilt graphql validation errors that would be a huge win. Thanks you! |
Looks like now support for multi error return is not supported now. Maybe apollo-server should consider this as a basic feature. |
It sounds like a potentially better solution to this problem would be the ability to supply a custom validation function for input types, no? Something like this: // Adapted from `GraphQLTypeResolver` in `graphql/type/definition.d.ts`
export type GraphQLTypeValidator<TSource, TContext> = (
value: TSource,
context: TContext,
info: GraphQLResolveInfo
) => MaybePromise<GraphQLError[]>;
// Copy-pasted from `graphql-tools/dist/Interfaces.d.ts`
export interface IResolverOptions<TSource = any, TContext = any> {
fragment?: string;
validate?: GraphQLTypeValidator<TSource, TContext>; // Added this
resolve?: IFieldResolver<TSource, TContext>;
subscribe?: IFieldResolver<TSource, TContext>;
__resolveType?: GraphQLTypeResolver<TSource, TContext>;
__isTypeOf?: GraphQLIsTypeOfFn<TSource, TContext>;
} The idea being that the Thoughts? |
I think just being able to return an array of errors from your resolver would be the easiest. I just came back to this. I tried to look into format error but the 2 errors just appear in the message extracted already. So I don't believe there is an easy way to throw multiple validation errors. I agree with @gengjiawen, that it would be great if you would consider adding this as a core feature. I get something like this out of formatError:
Is there some way you could add some type of check to the code that catches the error, something like: if(typeof errors = Array) {
errors = [...errros, ...anyOtherErrors]
} else {
errors = errors
} For even more context here is the GraphQL playground output where I return three Apollo UserInputErrors from my CoreProfile creation function
You can see how that are concatenated together in the message field. Thanks for all your hard work! |
I've searched a lot about valid implementation of error response to the client and I'm still trying to dig deeper. What I've learned at this point:
The main question for me with |
I put fields errors into schema itself because I believe it belongs there. https://github.com/este/este/blob/master/server/api/model.graphql#L28 Maybe I am wrong, but I don't know what's the point of UserInputError. |
This is where I have got to with my solution. From this code import { Txn } from "dgraph-js"; export async function validateInput(
{ timezoneName, currencyCode, emailAddress }: any,
txn: Txn
): Promise<{ timezoneUid: string | null; currencyUid: string | null }> {
const query = `
query timezoneAndCurrency($timezoneName: string, $currencyCode: string, $emailAddress: string){
timezone(func: eq(timezoneName, $timezoneName)){
uid
}
currency(func: eq(currencyCode, $currencyCode)) {
uid
}
{
email(func: eq(emailAddress, $emailAddress)) {
uid
}
}
}`;
const queryVars = {
$timezoneName: timezoneName,
$currencyCode: currencyCode,
$emailAddress: emailAddress
};
const inputQuery = await txn.queryWithVars(query, queryVars);
const { timezone, currency, email } = inputQuery.getJson();
const [timezoneUid, timezoneError] = uidFound(
timezone,
"Invalid timezoneName submitted. "
);
const [currencyUid, currencyError] = uidFound(
currency,
"Invalid currencyCode submitted. "
);
let errors = [];
if (timezoneError) {
errors.push(
new UserInputError(timezoneError, { invalidArgs: ["timezone"] })
);
}
if (currencyError) {
errors.push(
new UserInputError(currencyError, { invalidArgs: ["currency"] })
);
}
if (email.length > 0) {
errors.push(
new UserInputError("This email is already associated with an account. ", {
invalidArgs: ["emailAddress"]
})
);
}
if (errors.length > 0) {
// this is the key part
const error = new UserInputError('Error on user input', errors);
throw error;
}
return { timezoneUid, currencyUid };
}
function uidFound(value: [{ uid: string }], errorMessage: string) {
if (value[0] && value[0].uid) {
return [value[0].uid, null];
} else {
return [null, errorMessage];
}
} |
👋 I'll close this since this doesn't appear to be a bug with Apollo Server, but rather a question about how to use it or one of its components. Rather than asking it here in GitHub Issues — where efforts are focused on fixing bugs and adding new features — I'd ask that you take this question to the Apollo Server channel within the Apollo community on Spectrum.chat where there are community members who might be able to relate to a similar problem, or might be able to help you out more interactively. Thanks for your understanding! |
I was thinking I might just concatenate and serialize the message and dig it out using 'invalidArgs' as a key on fields with a matching name. What really annoyed me is that 'error' seems to be one of these JS basket case objects which is an object but does not readout like an object (maybe just a chrome thing), I wanted to find the contents of 'invalidArgs' and to do so, you do this: error.graphQLErrors[0].extensions.invalidArgs Pretty dumb right? If anyone has a prettier way, please let me know. |
Thanks for adding better error handling to V2. I have just run into some issues and wanted to share my thoughts and get some feedback from the community.
Current v2 errors documentation from the website
v2 errors documentation
A more complex case
The above solution works for a single variable mutation resolver, as soon as you have a multi variable input if falls down a bit. It would be nice if your documented example included what to do with multiple variables.
The issue I noticed is that with the current system you can only return one error message object from the resolver, I feel that it would be nice if you could return multiple BadUserInputErrors.
Something like this link
If you allowed the resolver to return multiple independent errors, this would then be consistent with how validation errors (like required) are currently returned, as each field is returned as it's own error.
Current validation errors sample
Also maybe the validation errors could have a "invalid arg" property added, to better match BadUserInputError?
One more thing to note which I have brought up here is that validation errors happen before the resolver is called, so if you have missing fields and validation errors you have to do a minimum of 2 requests even if the validation error is not on a missing field. This may be the desired outcome, it's just not obvious so it might be worthwhile documenting.
Happy to help out in any way I can if you know which direction this is going to go.
The text was updated successfully, but these errors were encountered: