-
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
Should I use GraphQL for fine-grained validation? #44
Comments
From slack talk:
|
FYI, if you follow this pattern, be sure to do validation for value coercion in addition to literal coercion. I think this is an area we will need to cover in deeper documentation. As long as the validation has semantic value, I think this pattern is a good one. For example, at Facebook we have a type called "URL" which explains very clearly what kind of value will come from such a field and allows us to do validation if it's provided as input. IMHO, doing this for "Email" makes a lot of sense, and I imagine you would want to use this not just to describe semantic input but also output |
Ok, thank you. And also in the comment, it is only specified |
Nice catch. I'm leaving this open as a reminder to improve comments and docs |
Could you give a quick explanations about the difference between both |
There are two scenarios:
|
Allright, clear as crystal. |
Not really related, but since I am digging into scalars....
|
Thanks for the note, I'll add a comment in code to explain what's going on here.
|
Sorry, if it sounds like a stupid question but the schema is supposed to be server-side only, or it can be embedded in client? I ask because if I want to share some enhanced types (ex: GraphQLExtrasString({min:2, max:10}), it is important to know if the build size matters. UPDATE: I am not just worried about the the build size, but also about features like |
It's expected that schema is represented client-side, usually in the form of code generation. You could imagine generating an NSObject, Java Object, or Flow type for every type represented in a GraphQL schema so clients can benefit from strong typed data. Of course, if your server presents custom scalars, this can be a challenge for these tools. You could imagine defining a |
So to be clear, this is wrong? /**
* Import dependencies
*/
import SomeLibWorkingOnlyServerSide from 'some-lib';
import {GraphQLScalarType} from 'graphql';
import {Kind} from 'graphql/lib/language';
import {GraphQLError} from 'graphql/lib/error';
/**
* Export enhanced String scalar type
*/
export default class String {
constructor(options = {}) {
return new GraphQLScalarType({
name: 'String',
coerce(originalValue) {
const {error, value} = SomeLibWorkingOnlyServerSide(originalValue, options);
if (error) {
throw new GraphQLError(error.message);
}
return value;
},
coerceLiteral(node) {
if (node.kind !== Kind.STRING) {
return null;
}
const {error, value} = SomeLibWorkingOnlyServerSide(node.value, options);
if (error) {
throw new GraphQLError(error.message, [node]);
}
return value;
}
});
}
} |
Well, at a high level no, but this implementation in particular has a lot of issues: To be clear: there's nothing wrong with defining custom scalars, that's exactly why the With this particular code there are a couple problems: First, just from a JavaScript point of view, a class constructor should not return an object of another type (in fact, I'm surprised Babel isn't choking on this). You should instead do: export var CustomString = new GraphQLScalarType({ Next, you can't name your custom scalar Here would be code that is perfectly fine: /**
* Import dependencies
*/
import SomeLibWorkingOnlyServerSide from 'some-lib';
import {GraphQLScalarType} from 'graphql';
import {Kind} from 'graphql/lib/language';
import {GraphQLError} from 'graphql/lib/error';
/**
* Export enhanced String scalar type
*/
export default function CustomString(typeName, options = {}) {
return new GraphQLScalarType({
name: typeName,
coerce(originalValue) {
const {error, value} = SomeLibWorkingOnlyServerSide(originalValue, options);
if (error) {
throw new GraphQLError(error.message);
}
return value;
},
coerceLiteral(node) {
if (node.kind !== Kind.STRING) {
return null;
}
const {error, value} = SomeLibWorkingOnlyServerSide(value, options);
if (error) {
throw new GraphQLError(error.message, [node]);
}
return value;
}
});
} |
Indeed. So, do you think it would be useful if I create a package like At first, my plan is to depends on Joi. And later make it more deps-free if people are enthusiastic. Note: Joi doesn't work on Browser. |
And thank you for your time/answers. I really appreciate it. |
This seems fine - there's nothing wrong with custom scalars, the caveat is that client-side tools may not know about them and without building the rules for custom scalars into those tools, won't be as intelligent and helpful as possible. |
Re: |
|
Second thought, having Last question, about naming convention, some type ends with |
I think there is a bug with the parser, a scalar created within a function is not recognised as a scalar. const emailType = createScalar('Email') // return new GraphQLScalarType(...)
[...] // in Query
args: {
email: {
name: 'email',
type: new GraphQLNonNull(emailType)
}
}, I get this error Same when I define the type on a field const userType = new GraphQLObjectType({
name: 'User',
fields: () => ({
email: {
type: emailType
}
})
}); I get this error However it works if I use the same custom scalar directly (not created and returned by a function). |
It's hard to see what your bug is without seeing how createScalar is implemented. It's almost certainly an issue within there. Can you post the full code you're using to reproduce this issue on jsbin or requirebin? |
Yes sure, I do it now. |
While writing a clean example to reproduce the bug, it seems to works. |
If you figure out what went wrong, let me know. I'd like to make the error messages as descriptive as possible. |
I have two questions related to custom scalar types:
|
Right now we expect invalid coercions to result in returning
This has since changed. It's not exported from the main module, but it is exported from the sub-module: import { Kind } from 'graphql/language'; However checking the string value is also acceptable if you prefer that |
@leebyron thank you for the update about the submodule. |
If anyone wants to know more on validation and custom scalars go here https://github.com/mugli/learning-graphql to custom types |
@pyros2097 A big |
Example: Input validation and user-friendly error messages in GraphQL mutations on Medium.com |
Thanks @ lot guys ! |
For example, I want to validate an
email
field.Should I define my own
email scalar type
or just useGraphQLString
and validate the value in theresolve
function?Similar question for checking the
length
of a string. If I have a field that map to aVARCHAR(100)
in a MySQL database, should I create a specific scalar that check for a valid length? Not sure if it's belongs to GraphQL... or if it is a good practise.Any thoughts?
Thanks
The text was updated successfully, but these errors were encountered: