Reputation: 9448
In this case, type "X" is Application
and type "Y" is type "Node" - I can see why this is happening, but my understanding of Relay isn't enough to understand how to fix it. The query generated by Relay is
query {
node(id: $some_id) {
...F0
}
}
fragment F0 on Application {
...
}
I have a schema that looks like
query {
application {
/* kind of a generic endpoint for fetching lists, etc */
invites(token: $token) {
name
}
}
viewer { /* the current user */ }
}
I'm trying to fetch a specific invite from outside a session (viewer
is null
).
I've tried
const application = Relay.QL`query { application }`
...
<Route ... queries={{ application }}/>
...
Relay.createContainer(Component, {
initialValues: { token: null },
fragments: {
application: () => {
fragment on Application {
invites(token: $token) {
...
}
}
}
}
})
which gives me the error
fragment "F0" cannot be spread here as objects of type "Node" can never be of type "Application" - or something to that effect.
I'm a little confused, because if I were to write a raw query and run it through GraphQL directly
query {
application {
invites(token: "asdasdasd") {
edges {
node {
name
}
}
}
}
}
it gives me what I'm looking for...
In the backend, my graph is defined like
export const Application = new GraphQLObjectType({
name: 'Application',
fields: () => ({
id: {
type: GraphQLString,
resolve: () => 'APPLICATION_ID'
},
invites: {
type: InviteConnectionType,
args: connectionArgs,
resolve: (application, args) => {
...
}
}
})
})
export default new GraphQLSchema({
query: new GraphQLObjectType({
name: 'query',
fields: {
node: nodeField,
application: {
type: Application,
resolve: (root, args, ctx) => {
return Promise.resolve({})
}
}
}
})
I've been looking at questions like this and some issues on the Relay github, but it's not clear to me how I should implement nodeInterface
.
edit: the short-long of the current nodeInterface
code is
export const {
nodeInterface,
nodeField
} = nodeDefinitions(
(globalId) => {
const { type, id } = fromGlobalId(globalId)
return db[type].findById(id)
},
(obj) => {
const name = obj.$modelOptions.name.singular
return types[name]
}
)
Application is not a db model, however, just a generic interface to fetch data through. I've tried checking to see if type === 'Application'
, and returning null (although I see why that doesn't work), returning Application
(the GraphQLObject), but that doesn't work... not really sure where to go from there.
Upvotes: 1
Views: 7348
Reputation: 9448
To give an update on this, I was on the right path.
The current nodeDefinitions
I had just needed a little extra:
nodeDefinitions(
(globalId) => {
const { type, id } = fromGlobalId(globalId)
if (type === 'Application') {
return Promise.resolve(Application)
}
return db[type].findById(id)
},
(obj) => {
if (obj.$modelOptions) {
/* sequelize object */
const name = obj.$modelOptions.name.singular
return types[name]
} else if (obj.name === 'Application') {
return Application
}
return null
}
)
I'm not sure if this is the best way to do it, but it seems to do the trick. The gist is that, if the type of node that I want to be returned is Application
, I return the GraphQL object - { ... name: "Application" ... }
we'll use the name
field from this in the next step (the second callback in nodeDefinitions
) to just re-return Application
. I think you could return a "custom" object or something else - it doesn't matter so long as you return something unique that you can define a mapping from to a GraphQLObject
type (required for the second callback).
Upvotes: 1
Reputation: 356
nodeInterface
you tell GraphQL
how to map the id to the corresponding GraphQL object.nodeInterface
identifies the GraphQL type.Below is simplified example how it may look like with Application:
// nodeInterface.
var {nodeInterface, nodeField} = nodeDefinitions(
(globalId) => {
var {type, id} = fromGlobalId(globalId);
// The mapping from globalId to actual object id and type.
console.log('globalId:', id);
console.log('type:', type);
if (type === 'Application') {
// getApplication is your db method to retrieve Application object.
// With id you could also retrieve a specific db object.
return getApplication();
} else {
return null;
}
},
(obj) => {
// Note that instanceof does an identity check on the prototype object, so it can be easily fooled.
if (obj instanceof Application) {
return ApplicationType;
} else {
return null;
}
},
);
// Application.
export const ApplicationType = new GraphQLObjectType({
name: 'Application',
fields: () => ({
// Auto-generated, globally defined id.
id: globalIdField('Application'),
_id: {
type: GraphQLString,
resolve: () => 'APPLICATION_ID'
},
invites: {
type: InviteConnectionType,
args: connectionArgs,
resolve: (application, args) => {
...
}
}
}),
// Declaring nodeInterface.
interfaces: [nodeInterface]
});
Note that during the initial fetch nodeInterface
is not even executed, so if nodeInterface
is returning nothing there won’t be errors at the initial fetch. If that doesn’t make sense or you’re still struggling you can post a link to the repo, I’ll look into it.
Upvotes: 2