Comments (11)
Having almost same problem. I try to use type of "Customer" which fetch some data from SQL and some from MongoDb.
export let customerType = new GraphQLObjectType({
name: 'customerType',
description: 'Customer schema',
fields: {
id: {type: GraphQLInt},
createdAt: {type: GraphQLString},
updatedAt: {type: GraphQLString},
deletedAt: {type: GraphQLString},
firstName: {type: GraphQLString},
lastName: {type: GraphQLString},
email: {type: GraphQLString},
password: {type: GraphQLString},
...
...
...
creditCardExpDate: {type: GraphQLString},
numLogins: {type: GraphQLInt},
quiz: {
type: new GraphQLList(quizType),
resolve: (THIS) => Customers.quiz({id: THIS.id})
}
}
});
All fields comes from SQL besides "quiz", resolve method for "quiz" return Mongoose promise which then return array of object. But on this query:
{
getCustomers(id: 45) {
id
firstName
quiz {
message
recommendedProducts
}
}
}
I get:
{
"data": {
"getCustomers": [
{
"id": 45,
"firstName": "Genesis",
"quiz": [
{
"message": null,
"recommendedProducts": null
}
]
}
]
}
}
Any solution so far?
from graphql-js.
Alright, well, I think I have a reasonable handle on the codebase now, thanks to a lot of node-inspectoring ;).
Here's a much simpler proof of the bug:
import {
graphql,
GraphQLSchema,
GraphQLObjectType,
GraphQLString,
GraphQLNonNull,
GraphQLList,
} from 'graphql';
var orgType = new GraphQLObjectType({
name: 'Org',
fields: () => ({
id: {
type: new GraphQLNonNull(GraphQLString),
resolve: () => {
return Promise.resolve(Math.random + '');
}
}
})
});
var schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'RootQueryType',
fields: {
orgs: {
type: new GraphQLList(orgType),
resolve: () => {
return [{ id: 1 }]
}
}
}
}),
});
var query = `
query Foo {
orgs {
id,
},
}
`
graphql(schema, query).then(result => {
console.log('RESULT', JSON.stringify(result, null, 2));
}).catch(console.log);
Which results in
RESULT {
"data": {
"orgs": [
{
}
]
}
}
but if you change orgType.id.resolve to just return a value, you get
RESULT {
"data": {
"orgs": [
{
"id": "0.8567653454374522"
}
]
}
}
as expected.
Here's what's happening:
- Because org is a list type, https://github.com/graphql/graphql-js/blob/master/src/executor/executor.js#L599-L603 we hit this line of code, where we're mapping over the list of orgs, and completing fields for each one.
- But each of those ends up returning a promise
- This means that resolveFieldOrError ends up returning something that looks like
[ Promise ]
- Which ends up here: https://github.com/graphql/graphql-js/blob/master/src/executor/executor.js#L470-L476
- Since we have an array with promises in it, we should be waiting for promise resolution, but we don't since an array doesn't pass the isThenable check.
- This means our field ends up just getting the array of promises, and since promises serialize to
{}
we end up with[ {} ]
in our result.
Fix?
The simplest fix would be to add the following between these two lines: https://github.com/graphql/graphql-js/blob/master/src/executor/executor.js#L469-L470 but I don't know the codebase well enough to know if this should move into isThenable in case there are similar issues elsewhere? Or if it should be in resolveFieldOrError maybe?
//convert Array(<Promise>), to Promise(<Array>)
if (Array.isArray(result) && result.some(isThenable)) {
result = Promise.all(result);
}
Thoughts? I can try and add a test to the star wars stuff that demonstrates this if required once I have a little more time.
from graphql-js.
Thanks for your in-depth exploration! There are some minor issues with your fix surrounding nullable vs non-nullable error handling, but it's directionally sound. I'll investigate further.
from graphql-js.
So here's the result of my further analysis:
The library hadn't supported this yet not as a bug but because we assumed if a resolve function would return a list of promises, that the resolve function itself would be responsible for calling Promise.all
, the way we use GraphQL at Facebook has resulted in this particular way of describing field resolution being underserved.
As I was thinking about this further, I think we're also missing a case where a list without non-null item type [T]
should handle a single item failure more gracefully.
So a list of promises:
resolve: () => [ fooPromise(), failingPromise(), barPromise() ]
should result in [ "foo", null, "bar" ]
where I'm pretty sure this currently will bubble up the error and become null
.
More investigation and test writing is necessary, but thanks again for highlighting!
from graphql-js.
Thanks! I'm not sure I agree though, in this case my resolve functions are only ever returning individual promises, but it's the lib itself which is creating an array of promises when it maps over a list type, no?
Philip Roberts
On 4 Jul 2015, at 16:54, Lee Byron [email protected] wrote:
So here's the result of my further analysis:
The library hadn't supported this yet not as a bug but because we assumed if a resolve function would return a list of promises, that the resolve function itself would be responsible for calling Promise.all, the way we use GraphQL at Facebook has resulted in this particular way of describing field resolution being underserved.
As I was thinking about this further, I think we're also missing a case where a list without non-null item type [T] should handle a single item failure more gracefully.
So a list of promises:
resolve: () => [ fooPromise(), failingPromise(), barPromise() ]
should result in [ "foo", null, "bar" ] where I'm pretty sure this currently will bubble up the error and become null.More investigation and test writing is necessary, but thanks again for highlighting!
—
Reply to this email directly or view it on GitHub.
from graphql-js.
Ah yes, I think there are two subtle overlapping issues here. I see from your test case that there is another failure to resolve the promise before continuing. Thanks for providing them, I'll make sure they get encoded as runtime unit tests.
from graphql-js.
Thanks @leebyron! Yeah, it was quite hard to produce a minimal testcase that demonstrated the behaviour, while still demonstrating the intent :)
from graphql-js.
@latentflip please check out 0fd8be1 and let me know if the test cases I added are aligning with the issues you ran into.
from graphql-js.
Version 0.1.2 is on npm that includes that commit
from graphql-js.
@leebyron yup, just ran it on the test-cases + orm integration I've been working on and it's looking good. Thanks for the quick turnaround! 🎉
from graphql-js.
Thank YOU for playing with it in such an early state and investigating. I may not have found this issue otherwise.
from graphql-js.
Related Issues (20)
- Uncaught SyntaxError: missing name after . operator HOT 1
- FieldResolver performance HOT 2
- Inline fragment from an interface to an interface that extends it is invalid
- Resource exhaustion exploit when parsing queries HOT 7
- In GraphQL how to set WriteOnce functionality in specific property in the collection schema HOT 1
- Generator generates alias imports not compatible with TypeScript Type-Only Imports and verbatimModuleSyntax HOT 2
- Printing the @oneOf directive HOT 1
- Allow shallow sorting with lexicographicSortSchema HOT 2
- Passing arguments into the resolvers by just using `buildSchema` and `graphql`? HOT 3
- Error after upgrading from 16.6 to 16.8.1 with globalThis.process.env.NODE_ENV HOT 12
- Add: Link to new api docs in the README of this project.
- Introspection result missing interfaces
- Detect subscription successfully started HOT 1
- Errors thrown when iterating over subscription source event streams (AsyncIterables) should be caught HOT 2
- graphql resolver circular dependency type error (bi-directional relation) HOT 1
- Current main is up to 50% slower than previous major version HOT 10
- Type definition for `DefinitionNode` appears to be wrong HOT 4
- AST: `IntValueNode` and `FloatValueNode` store values as string HOT 2
- Array-type resolvers: an intuitive solution to the n+1 problem HOT 1
- Performance of stack traces in errors results in high response latency (>1 second) HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from graphql-js.