Introduction to GraphQL Resolver Arguments
This course has been updated! We now recommend you take the Server-Side GraphQL in Node.js course.
Transcript from the "Resolver Arguments" Lesson
>> Scott Moss: Creating resolvers. Basically, they just need to return the same shape as described in the schema or delegate to another resolver. That's basically it. So they either have to return the shape that the schema said that they are gonna return, or they have to delegate to some other resolver that eventually is gonna return the shape.
[00:00:16] Or that resolver delegates to another resolver, or that resolver delegates to another resolver, but eventually the shape has to be returned. Otherwise, you're either gonna have a hanging API or this is gonna break, both of which are bad. So basically, resolvers take a few args here. So let's just create some resolvers here and we'll get going.
[00:00:36] So I'm just gonna go into product.resolvers here. Let's do overview of this file here. So if you go into product.resolvers, if you're in the lesson two branch or even the lesson three branch, you'll see that at the bottom I'm exporting a default here. And it's an object with a Query property, a Mutation property, then a Product property.
[00:00:59] And these are gonna be all the resolvers. They actually match one in one for what's in our schema. So if I go look at our schema here, our product.gql. If you go all the way down, remember, I said you have to have resolvers for queries and mutations. Well, there's nothing here yet cuz you'll eventually be doing these, but I already have the placeholders for them.
[00:01:18] So if I wanna write resolvers for my queries, they go inside this Query object. If I wanna write resolvers for my mutations, they go inside this Mutation object. If I wanna write resolvers for my product type, specific fields that I wanna resolve like the ID field or the createdBy field, they go in this Product type.
[00:01:33] They match one for one for what's in the schema. Product with a capital P matches this type, Product with a capital P.
>> Scott Moss: Query matches this type, Query with a capital Q. Mutation matches this type with a capital M. That's how it does the matching, it looks at the names there.
[00:01:48] So this is how it knows what resolvers you're trying to write, so then you would go in here and you would write your resolvers. Let's just make some resolvers then. So we'll just make a resolver for one of the queries here. Let's just call it, actually we'll just make a new type here.
[00:02:03] We'll call this,
>> Scott Moss: getData. So we're gonna make a new query called getData.
>> Scott Moss: And it's not gonna take any arguments. And it's just gonna return back a product, let's just say it's gonna return back a product. There we go, so now that I have this query and I have getData, that means now I have to come inside of the query here.
[00:02:32] And I have to add a resolver by the same name that's defined here, so getData. So I have to add a field here called getData. It has to be the exact same name. And it's a function. So that's step one of creating a resolver. So now, that resolver is created.
[00:02:46] It's not doing anything, but it's created.
>> Scott Moss: Following me so far? So I just create a resolver, same name matches here. It's part of the Query.getData, so Query.getData.
>> Scott Moss: So then the first argument that a resolver takes is the starting object. Or if this resolver is being as a delegate a resolver, it will be what the parent resolver returned.
[00:03:15] But it's the starting object. In this case because any resolver that's part of a query or mutation, there's no resolver before it. It's the root resolver. It's the first one. It's the top of the tree. Nothing is before it, so that first argument is almost always gonna be no unless inside of your server file, you passed in a starting value, which you can.
[00:03:39] In some Graph QL servers, you can say start with this value, and you can start with whatever value you want. In that case, whatever that value you pass in as your starting value would be that first argument and your top level resolvers on queries and mutations. But I've almost never had to do that.
[00:03:54] I can't think of an example why I would pass in a starting value for a top level thing. So most likely we won't do that. So I normally just put a placeholder here, something like that, cuz I'm never gonna use it, at least for the top level queries.
[00:04:06] For type resolvers, you will use them. So the second argument is gonna be the args that were potentially passed to you from the query. So if we go look here, getData doesn't take any arguments. There's no arguments here, there's no parentheses. So there's no arguments. So this would just be, there's nothing in here.
[00:04:26] If any arguments were passed here, we'd see them here. For instance, product takes an argument called id. So on this args object, there'd be an id property that is this value. That's how we get the arguments.
>> Scott Moss: The third argument is gonna be the context. In the context, you can think of it's like shared state between all resolvers.
[00:04:46] It's an object that gets passed to every single resolver, and you create it when your server starts. So it's a great place to put user authentication, shared logic, caching storage mechanisms, database models, stuff like that. People attach a lot of things to their context, that way it's a lot easier to test these resolvers because you can just mock up the context objects versus importing everything at the top of a file.
[00:05:07] And then you could pass state between different resolvers. This resolver did this, now I pass it on to here. Here is some state that you can keep track of. For instance in the server.js, you can see I'm allowed to create a context here, this is the initial context.
[00:05:23] And I'm just creating an object that has a user property that's set to null. So there's no user on there, you'll be fixing that later. But yeah, I'm creating the initial context right here, but you can put whatever you want here. This same context object that you're creating is gonna be passed to every single resolver as a third argument.
[00:05:38] It's always gonna be the third argument, and it's always going to be that context object, every single request.
[00:06:24] It will tell you every single thing about that so you can pass that to a database and say, only select these fields and stuff like that stuff like that, or if you're doing some type of tooling or whatever. Some other tools use this to figure out different caching mechanisms, like to cache this field and not cache this field, stuff like that.
[00:06:40] So you can cache on a field-by-field basis. So that part, the info is very, very, very advanced. You will almost never use it unless you were doing some type of optimizations. I've only had to use it twice. So, yeah, you almost never use it.