Reputation: 5968
Apologies if this is a stupid question. this is the code for relay/graphql pagination that's confusing me:
const GraphQLTodo = new GraphQLObjectType({
name: 'Todo',
fields: {
id: globalIdField('Todo'),
text: {
type: GraphQLString,
resolve: (obj) => obj.text,
},
complete: {
type: GraphQLBoolean,
resolve: (obj) => obj.complete,
},
},
interfaces: [nodeInterface],
});
/* When pagination is needed, make a connection */
const {
connectionType: TodosConnection,
edgeType: GraphQLTodoEdge,
} = connectionDefinitions({
name: 'Todo',
nodeType: GraphQLTodo,
});
const GraphQLUser = new GraphQLObjectType({
name: 'User',
fields: {
id: globalIdField('User'),
todos: {
type: TodosConnection,
args: {
status: {
type: GraphQLString,
defaultValue: 'any',
},
...connectionArgs,
},
resolve: (obj, {status, ...args}) =>
connectionFromArray(getTodos(status), args),
},
totalCount: {
type: GraphQLInt,
resolve: () => getTodos().length,
},
completedCount: {
type: GraphQLInt,
resolve: () => getTodos('completed').length,
},
},
interfaces: [nodeInterface],
});
const Root = new GraphQLObjectType({
name: 'Root',
fields: {
viewer: {
type: GraphQLUser,
resolve: () => getViewer(),
},
node: nodeField,
},
});
You can see that on the GraphQLTodo field, it has text and complete fields with resolve function passed an obj parameter, how is obj passed there? is it from GraphQLUser resolve? I've read on docs that source(in this case obj) - The object resolved from the field on the parent type. is it not from the root query? how is obj here created?
Upvotes: 1
Views: 440
Reputation: 1154
Here is where (some of) the magic happens:
const {
connectionType: TodosConnection,
edgeType: GraphQLTodoEdge,
} = connectionDefinitions({
name: 'Todo',
nodeType: GraphQLTodo,
});
You have now told GraphQL that a TodosConnection
is going to be made up of GraphQLTodo
nodes. Now, let's take a look at where the objects are actually fetched for the connection in your GraphQLUser
object, which is on the todos
field:
todos: {
type: TodosConnection,
args: {
status: {
type: GraphQLString,
defaultValue: 'any',
},
...connectionArgs,
},
resolve: (obj, {status, ...args}) =>
connectionFromArray(getTodos(status), args),
},
So where does the object come from? The key part here is the getTodos
function, which is responsible for actually getting an array of the objects from your data source. Since this field is a TodosConnection
and we've already specified in the connection definitions that the nodes are GraphQLTodo
s, GraphQL knows that the text
and complete
fields are resolved by getting (in this case) identically named fields on the objects that have been returned. In other words, the returned object is passed to the resolve
method on each field.
You have two fields exposed on Root
: viewer
and node
. Ignoring node
for a moment, you have just one way to actually query todos. Since viewer
is of type GraphQLUser
, and GraphQLUser
has that todos
field, they can be fetched only as a subfield of viewer
, like this:
{
viewer {
todos(first: 10) {
edges {
# each node is a Todo item
node {
text
complete
}
}
}
}
}
But what about that node
field? Relay wants to be able to fetch any object using a top-level query, i.e. on your Root
field, when given a unique globalId
, which is just a base64 encoding of the type name and the id, so Todo:1
is encoded to VG9kbzox
. This is set up in the nodeDefinitions
(which you haven't included here, but probably have). In those definitions, the globalId
is parsed back into the type
(Todo
) and id
(1
), and once again you then tell it how to fetch the correct object from your data source. It might look something like:
const { nodeInterface, nodeField } = nodeDefinitions(
(globalId) => {
const { type, id } = fromGlobalId(globalId);
if (type === 'Todo') {
return getTodo(id)
} else if (type === 'User') {
return getUser(id)
}
...
Because you're implementing the nodeInterface
in both your GraphQLTodo
and GraphQLUser
types, Relay will be able query for either of them from the Root
's node
field.
Upvotes: 2