I'm having trouble resolving graphql nested types. I can successfully get the UserMetrics
nested resolver to fire, but the parent resolver object (user) is null. Am I misunderstanding the GraphQL resolver map?
Schema:
type User {
id: String!
metrics: UserMetrics
}
type UserMetrics {
lastLogin: String!
}
Resolver:
Query: {
user(_, { id }, ctx) {
return { id };
}
},
User: {
metrics(): ({}), // UserMetrics.lastLogin doesn't fire without this
},
UserMetrics: {
lastLogin(user) {
console.log(user); // null
}
},
You can use the object (one-to-one) or array (one-to-many) relationships defined in your schema to make a nested query i.e. fetch data for a type along with data from a nested or related type. The name of the nested object is the same as the name of the object/array relationship configured in the console.
A resolver is a function that resolves a value for a type or field in a schema. Resolvers can return objects or scalars like Strings, Numbers, Booleans, etc. If an Object is returned, execution continues to the next child field. If a scalar is returned (typically at a leaf node), execution completes.
The __typename field returns the object type's name as a String (e.g., Book or Author ). GraphQL clients use an object's __typename for many purposes, such as to determine which type was returned by a field that can return multiple types (i.e., a union or interface).
The GraphiQL client allows you to create variables for use in your queries. To add a query variable, click the Query Variables pane and enter a JSON object that defines your variable. To use a variable in your query, prepend the $ character to your variable name and use it to replace the desired value.
This can be a little confusing to wrap your head around, and unfortunately the docs don't provide a solid example of how nested types work. According to the docs, the first argument the resolver takes is:
The object that contains the result returned from the resolver on the parent field
So, the parent (User) resolves a certain field (metrics). If this field is a scalar, it doesn't need to do anything else and that's what's returned. Otherwise the value the parent resolved is passed down to the resolvers associated with the "child" type (UserMetrics). Each of those resolvers then uses that value to resolve the fields it is responsible for.
In the code you provided, User
is resolving metrics
as an empty object (i.e. {}). This is the value that's passed to the resolver for any field inside UserMetrics. Strictly speaking, in this case, running console.log on that value will return {}, not null.
Try this: Comment out your resolver for the metrics
field, and instead of { id }
, have your query return { id, metrics: { lastLogin: 'foo' }}
. Run that and you should see a console output showing the object that was passed to the resolver for lastLogin
, in this case { lastLogin: 'foo' }
.
Step by step, what's happening here:
user
field of the query is resolving to { id: 'whateverIdYouProvided', metrics: { lastLogin: 'foo' }}. Since it's returning a User
type, and not a scalar, it passes this value down to the resolvers for any fields inside User
.id
has no resolver specified in this case, so it uses the default resolver. This just looks at the object it received, and if it finds a property that matches the name of its field, it resolves to the value of that property. If it can't find any property by that name, it returns null. In this case, id is a scalar, so it just resolves to 'whateverIdYouProvided' and doesn't need to go any further.metrics
resolver, this field will also use the default resolver. In this case, it resolves to a type (UserMetrics), so it looks at the object it was given, finds a property called metrics
and passes that value (i.e. { lastLogin: 'foo' }
) to any resolvers for fields inside UserMetrics.{ lastLogin: 'foo' }
-- to work with. If you don't specify a resolver here, again the default resolver will be used and it will happily find a property called lastLogin and resolve to it's value ('foo'). If you provide a custom resolver, as you have done, obviously it will resolve to whatever you return (in the case of your code, null).If the lastLogin
resolver isn't being called at all, verify that you've actually included that field in your query! Any fields not included in the query don't need to be resolved, and so their resolvers aren't called.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With