Reputation: 33
I'm setting up testing for my graphql. While doing so i realise that sometimes the data object on error cases outputs like that :
{
errors: [...],
data: null
}
and sometimes :
{
errors: [...],
data: {
updateCity: null
}
}
Those test are on mutations. This is an example of both code :
resolver case 1 :
updateUser(parent, args, context, info) {
logger.debug('Mutation > updateUser resolver');
return userController.user.update.user(parent, args.userInfo, context, info);
},
schema case 1 :
extend type Mutation {
updateUser(userInfo: UserInfo!): User!
}
controller case 1 :
user: async (parent, args, context, info) => {
try {
logger.debug('User Controller : update User');
await controller.validate(args);
const userModel = new UserModel('member');
if (!(await userModel.findOne(args.id))) errorscb.userInputError('User does not exist');
let id = args.id;
let newArgs = args;
delete newArgs.id;
return userModel.updateById(id, newArgs);
} catch (e) {
logger.warn({ stack: e.stack, message: e.message });
throw e;
}
},
schema case 2 :
extend type Mutation {
updateCity(id: Int!, name: String, countryId: Int): City
}
resolver case 2 :
updateCity(obj, args, context, info) {
logger.info('City > updateCity resolver');
return cityController.city.update.city(obj, args, context, info);
},
controller case 2 :
city: async (parent, args, context, info) => {
try {
logger.info('City Controller : update city');
await controller.validate(args);
const cityModel = new CityModel('city');
if (!(await cityModel.findOne(args.id)))
errorscb.userInputError('City does not exist');
let id = args.id;
let newArgs = args;
delete newArgs.id;
return cityModel.updateById(id, newArgs);
} catch (e) {
logger.warn({ stack: e.stack, message: e.message });
throw e;
}
I'd like to obtain a consistent output, anyone knows how to fix that ?
Upvotes: 3
Views: 1011
Reputation: 84757
This is actually expected behavior.
The difference between updateUser
and updateCity
in your is that the latter returns a nullable type (City
), while the former returns a non-null one (User!
). The difference in the responses comes from the fact that errors propagate up the response until they hit a nullable field. From the spec:
If an error is thrown while resolving a field, it should be treated as though the field returned null, and an error must be added to the "errors" list in the response.
If the result of resolving a field is null (either because the function to resolve the field returned null or because an error occurred), and that field is of a Non-Null type, then a field error is thrown. The error must be added to the "errors" list in the response.
...
Since Non-Null type fields cannot be null, field errors are propagated to be handled by the parent field. If the parent field may be null then it resolves to null, otherwise if it is a Non-Null type, the field error is further propagated to it’s parent field.
In other words, by throwing an error during a field's resolution, we effectively resolve that field to null. But when we tell GraphQL a field has a Non-Null type, and that field resolves to null
, GraphQL cannot return the field with a null
value (because that would break the contract of the schema). So it makes the entire parent field null. If the parent field is also non-nullable, it nulls that field's parent field, and so on... until it either reaches a nullable field or the root of the request (the data
field).
Compare: Schema 1
type Query {
a: A
}
type A {
b: B
}
type B {
c: String
}
Schema 2
type Query {
a: A
}
type A {
b: B
}
type B {
c: String!
}
Schema 3
type Query {
a: A!
}
type A {
b: B!
}
type B {
c: String!
}
If we request the field c
and the resolver for field c
throws, the responses are as follows:
Schema 1
{
"data": {
"a": {
"b": {
"c": null
}
}
}
}
Schema 2
{
"data": {
"a": {
"b": null
}
}
}
Schema 3
{
"data": null
}
Upvotes: 5
Reputation: 33
extend type Mutation {
updateUser(userInfo: UserInfo!): User!
}
Found the fix, requiring User was unnecessary and removing the exclamation mark fixed my issue (eh... somehow)
updateUser(userInfo: UserInfo!): User!
Upvotes: 0