Keeping Entire Schema in One file is very ugly and hard to maintain. If we keep it in seperate files there is a Circular Dependency Issue with Interface.
Is there a solution to this?
CategoryEdge.node field type must be Output Type but got: undefined.
This is not an issue with GraphQL-JS, but it's true that the solution to this is not well documented.
You can use a thunk for your fields like so:
import { OtherType } from './otherType';
const PersonType = new GraphQLObjectType({
name: 'PersonType',
fields: () => ({
someField: { type: OtherType, resolve: () => 5 },
})
});
I highly recommend using separate files for your schema definitions. A good practice is one type per file with well named files.
As Jonas pointed out, whenever there is a circular dependency between types, you need to use a "thunk" (a function that returns a value, called later) for your fields
I have a circular dependency in my Connection definitions
export default connectionDefinitions({
name: 'Product',
nodeType: ProductType,
connectionFields: {
count: {
type: GraphQLInt,
}
}
});
ProductType is undefined in this case
how can I use a "thunk" to solve this?
@helfer @leebyron can I use a thunk on connectionDefinitions?
@sibelius I don't know the answer, but since your question is about graphql-relay-js you might have more luck getting it answered there.
@sibelius AFAIK you can't use a think on connectionDefinitions yet. I posted my current workaround in https://github.com/graphql/graphql-js/issues/612
Also, don't use CommonJS.
From this reference: https://gist.github.com/fbaiodias/77406c29ddf37fe46c3c
Some important notes ... CommonJS modules export values, ES6 modules export immutable bindings. Use ES6 if possible to avoid problems.
Was there a updated solution to this? I tried doing all fields as thunk and @holmesal hacky fix without any luck. I created a Stack Overflow question on it
Edit: Solution was found! More info in the post but this is the fix for importing a circular type:
pageEdge: {
type: require('./connections/PageConnection').default,
args: connectionArgs,
resolve: async (page, { ...args }, { loaders }) => {
if (page.pageEdge) {
const pageEdge = await loaders.pageLoader.loadMany(page.pageEdge);
const connection = connectionFromArray(pageEdge, args);
return connection;
}
},
},
Going back to the original issue...
So I have split up my schema into a number of seperate files, but now seem to have hit a circular dependency issue I can't resolve.
Basically I want to keep each query and mutation in a separate file.
So at the top-level I might have a schema like:
# schema.js
import { updateProject } from './mutations';
import { projectType } from './types';
const projectField = {
type: projectType,
description: 'Look up a project by ID or slug',
....
};
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'RootQueryType',
description: 'The query root of GraphQL Interface',
fields: () => ({
project: projectField
})
}),
mutation: new GraphQLObjectType({
name: 'RootMutationType',
fields: () => ({
updateProject
})
})
});
With a projectType definition in a separate file
export const projectType = new GraphQLObjectType({
name: 'Project',
description: 'A project',
fields: () => {
name: {
type: GraphQLString,
description: 'Project',
resolve: () => 'test'
},
}
});
Now suppose that my definition of updateProject is as follows
import { GraphQLNonNull, GraphQLInt, GraphQLString } from 'graphql';
import { projectType } from '../types';
import * as DB from 'db';
export default {
type: projectType,
args: {
...
},
resolve: (project, { }, request, info) => {
...
}
};
When this setup is run, it will fall over with:
Error: Attribute.field field type must be Output Type but got: undefined.
So what happens is:
updateProject mutationupdateProject imports projectTypeprojectType.fields thunk has not been run, hence the error above.It feels like I need a thunk on the type field of my mutation? Does anyone have any suggestions?
If a mutation or query has a type of something that uses a thunk for it's fields, then any field that uses it must also be inside a thunk? Seems impossible then to define it in a standalone file?
If a mutation or query has a type of something that uses a thunk for it's fields, then any field that uses it must also be inside a thunk?
@joewoodhouse Not necessary.
Error: Attribute.field field type must be Output Type but got: undefined.
It's mean you have Attribute type with field called field, right?
Just convert type of this field to thunk.
It feels like I need a thunk on the type field of my mutation? Does anyone have any suggestions?
Your example is incomplete since it missing Attribute type so it's hard to provide concrete advice. Please provide the minimal reproducible source code of all files involved.
Ooo can a type be a thunk? I didn't even try that
@joewoodhouse Sorry got confused, not it can't :(
I checked the source code and only fields, interfaces and types(for Union) could be a thunk.
But allowing to be a Thunk fields should be enough for possible scenarios since fields can't depend on fields they depend on type as a whole.
Looking through the code you posted above I see this problem:
// Without making projectField a function you don't break circular dependencies
const projectField = () => ({
type: projectType,
description: 'Look up a project by ID or slug',
....
});
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'RootQueryType',
description: 'The query root of GraphQL Interface',
fields: () => ({
project: projectField() // You need to make call here
})
}),
});
Be also careful with an index.js that re-exports all the types (try uncommenting line 15 or 16 in the src/index.js
Great!, Thunk really helped!
Thanks!
Most helpful comment
This is not an issue with GraphQL-JS, but it's true that the solution to this is not well documented.
You can use a thunk for your fields like so: