Current behavior
When aliasing a field name for a required field, server throws error Cannot return null for non-nullable field.
Stack trace below:
Error: Cannot return null for non-nullable field User.email.
at completeValue (C:\Users\me\Documents\matchsal-prisma\node_modules\graphql\execution\execute.js:648:13)
at completeValueWithLocatedError (C:\Users\me\Documents\matchsal-prisma\node_modules\graphql\execution\execute.js:595:21)
at completeValueCatchingError (C:\Users\me\Documents\matchsal-prisma\node_modules\graphql\execution\execute.js:564:12)
at resolveField (C:\Users\me\Documents\matchsal-prisma\node_modules\graphql\execution\execute.js:510:10)
at C:\Users\me\Documents\matchsal-prisma\node_modules\graphql\execution\execute.js:357:18
at Array.reduce (<anonymous>)
at executeFields (C:\Users\me\Documents\matchsal-prisma\node_modules\graphql\execution\execute.js:354:42)
at collectAndExecuteSubfields (C:\Users\me\Documents\matchsal-prisma\node_modules\graphql\execution\execute.js:799:10)
at completeObjectValue (C:\Users\me\Documents\matchsal-prisma\node_modules\graphql\execution\execute.js:781:10)
at completeValue (C:\Users\me\Documents\matchsal-prisma\node_modules\graphql\execution\execute.js:677:12)
Reproduction
Relevant schema.graphql:
type Query {
me: User
}
type User {
email: String!
firstName: String!
id: ID!
isEmailVerified: Boolean!
lastName: String!
locale: USER_LOCALE!
updatedAt: DateTime!
flags: [USER_FLAGS!]
}
Client query:
query UserAuthQuery {
me {
...userInfo
}
}
fragment userInfo on User {
id,
locale,
userEmail: email,
firstName,
lastName,
flags,
}
If I change userEmail: email to just email then the query works without issue.
Expected behavior?
The server is smart enough to know that userEmail is the aliased email field, and thus the requirement of email not being null is satisfied with the above query
Does this occur for a direct query against your Prisma service, or when using prisma-binding?
If the latter, this sounds similar to this issue.
It's happening when querying against a graphql-yoga server. Here is my resolver:
async me(parent, args, ctx, info) {
const fullAuth0Id = await getAuth0Id(ctx)
return ctx.db.query.user({ where: { fullAuth0Id } }, info)
}
I am able to replicate this without involving graphql-binding, prisma-binding, graphql-yoga. This is happening on remote schemas in graphql-tools (which explains it being visible in graphql-yoga setup).
Replication details are mentioned here (use branch 1896 in the repository for this issue).
https://github.com/divyenduz/bind-gql-tools/blob/1896/README-1896.md
Response from delegateToSchema looked alright, maybe, there is an issue with schema generation for remote schemas.
Will investigate this further tomorrow.
I can confirm that this (https://github.com/apollographql/graphql-tools/issues/519) is the same issue in graphql-tools as the field favoriteRepos is a required one.
Also, the solution suggested here (https://github.com/apollographql/graphql-tools/pull/591), to use makeRemoteExecutableSchema is also broken as I am able to replicate (https://github.com/divyenduz/bind-gql-tools/blob/1896/README-1896.md) this using execute and parse from graphql-js directly on a schema created with makeRemoteExecutableSchema without using delegateToSchema.
=> My bad, I was indeed using delegateToSchema at another place that I overlooked. Had a conversation with Mikhail, We need to use defaultMergedResolver since GraphQL default resolver does not use aliased names. However, Mikhail pointed out that this doesn't yet work with overlapping aliases (two aliases on same field).
Investigating further on how to resolve/get around this.
Update + questions!
defaultMergeResolver is not exposed by graphql-tools, we might have to write our own resolver function that is alias aware.
There are two ways we can go about this (IMO)
Either override the default resolver in graphql-js with an alias aware resolver - better, can support multiple aliases.
=> looking at the correct place where this might go in graphql-binding/prisma-binding - any hints?
Strip the aliases in the result of delegate to schema and replace it with actual field name - not complete solution as same field can have multiple aliases - de-aliasing won't be possible
Current workaround (when using graphql-yoga), can be to pass a custom field resolver in ApolloServerOptions, that can look like!
const customFieldResolver = // Alias aware field resolver!
const options = {
fieldResolver: customFieldResolver
}
server.start(options, () => console.log("Server is running on http://localhost:4000"));
I tested (not automated) to validate that this works, albeit, I am aware that this is not the complete solution and that would most probably be replacing default graphql resolver in prisma-binding/graphql-binding with a custom resolver which is alias aware.
References:
I (now) think that bindings are working as expected.. the error arises when execute is called after resolution of data from binding. Because that result has aliases and default resolver of graphql-js does not support aliases!
The result of the binding has aliases mentioned in the original query which is correct behavior IMO.
But the resolution in graphql-yoga happens with default resolver of graphql-js that fails on aliases in the result. Replacing default resolver with an alias aware resolver works.
This PR started to get around the issue: https://github.com/graphcool/graphql-yoga/pull/216
Strange that via playground works fine, but via front apollo-client (on same port that used by playground) has error. But after execute via playground, execute correctly via apollo-client too.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Related topic: https://github.com/graphql-binding/graphql-binding/issues/80
Don't know if the issue is considered as solved, but I have it right now on my backend (graphql-yoga 1.14.12).
I can resolve the problem by adding a resolver to the field.
the query:
{
stuff {
bla: blob
}
}
the schema:
type Stuff {
blob: String
}
the resolver:
... = {
blob: stuff => stuff.blob,
};
And it works.
If I remove the resolver, the field returns null (as soon as I put an alias).
So this is not related to the fact that the field is required or not.
The issue happens when we alias a field that doesn't have a resolver (the bug must reside in the default resolver applied by graphql-tools, I suppose).
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Most helpful comment
Don't know if the issue is considered as solved, but I have it right now on my backend (graphql-yoga 1.14.12).
I can resolve the problem by adding a resolver to the field.
the query:
the schema:
the resolver:
And it works.
If I remove the resolver, the field returns null (as soon as I put an alias).
So this is not related to the fact that the field is required or not.
The issue happens when we alias a field that doesn't have a resolver (the bug must reside in the default resolver applied by graphql-tools, I suppose).