Introduction to GraphQL Resolvers
This course has been updated! We now recommend you take the Server-Side GraphQL in Node.js course.
Transcript from the "Resolvers" Lesson
>> Scott Moss: What are resolvers? So we've used them a little bit, we've talked about them. Anybody wanna try to tell me what they are?
>> Scott Moss: Take a guess.
>> Speaker 2: Resolve your queries?
>> Scott Moss: They resolve queries, that's exactly what they do. They resolve queries, mutations, they resolve everything actually, not just queries.
[00:00:23] They resolve pretty much anything that could be of value. Enums, queries. You'll find out interfaces, unions, mutations. Anything that could eventually be some type of value, we can write a resolver for it. And this is what makes GraphQL magical, is the resolvers. So they're like controllers if you're building out some traditional API like a rest API with express we have our controller that talk to the database to get the data.
[00:00:49] Resolvers are like the same thing, this is where you do that. This is where you talk to the database or your data source, inside the resolvers. Except they resolve all the way down. What does that mean? Well therefore, resolvers resolving a type, and a type has a field that's referring to another type, and then that type has a field that's referring to another type.
[00:01:10] You have to have a resolver for every single type all the way down, so it resolves all the way down depending on the query that came in. So if the query asks for those connections, I want this type that refers to this type, that refers to this type, then you have to have resolvers for every single level.
[00:01:25] And the resolvers might be the same, you might have a resolver that's delegating to itself because it's asking for the same type. Who knows, it really depends on what the query looks like. So it's kinda confusing to think about, but we'll go through all of it and you'll see what I'm talking about.
[00:01:38] So I said there like REST API controllers. They're responsible for retrieving data. That data, obviously, can come from a database, but it can come from anywhere else, if you get really advanced with GraphicQL, you can stitch schemas together, and you can have resolvers that resolve data from another GraphicQL schema.
[00:01:57] Or they delegate to a REST API, or they just talk to some third party service, it doesn't have to be a database, they can resolve data from anywhere in memory that we need to. This is where you might get stuff from if you're using. So they just resolve data as long as it fills that shape then it's good.
>> Scott Moss: Every query and mutation your schema has, must have a resolver that returns the specified type. So if we go look at, these queries, myCat and hello, actually let's look at the ones we made, it's a better example. If we look at these queries, here, so we have product query and a products query.
[00:02:41] So that means we for sure have to write resolvers for these queries. And we for sure have the right resolvers for these mutations. Otherwise, in this case, Apollo would actually break if you tried to start it. If you try to start Apollo and it didn't find resolvers with the same names as the queries and mutations that you put in your schema, it'll say you added some resolvers in your schema, but they're not in your resolvers.
[00:03:06] And vice versa, if you made resolvers and you didn't put them in your schema, it also breaks. So it's gonna look for the names. And that's just by default. So you have to have them there, otherwise nothing's gonna happen. So what's the point of putting them there? You're just waiting for somebody to break your API.
[00:03:21] So for all your queries and argumentations there have to be resolvers.
>> Scott Moss: Types and fields on types often have resolvers as well. So what does that mean? Well, so we talked about these queries and mutations having resolvers. Well, types can also have resolvers, too. For instance, I have this ID property that I just put here.
[00:03:51] MongoDB doesn't have a ID property, it has an _ID property. So if I just query the database and return that object, and I'm expecting an ID property back, cuz that's what the query asked for. It'll break cuz there's no ID property there. So you'd have to write a resolver for this field on product that grabs product._ID and returns that.
[00:04:13] And that's how you would resolve that, so you can write resolvers for types as well and fields on types. And stuff like that or for instance another good one is the created by or user. This is a user right here. It's a user object in a database but by default it stores an ID in the database as a reference.
[00:04:29] So how would you do that? Well one way is you could try to populate it in Mongo in the top level resolver that might happen on product or products but you wouldn't know if the query asked for it or not unless you inspected he AST which is really hard.
[00:04:45] So the only other way to do it is to write a resolver for the createdBy field for the product and then you can take the product that createdBy id and then go to the database and find the user for it so then you will resolve that. And so forth and so forth and that user had something else, you can resolve those fields.
[00:05:01] So you can resolve all these fields individually to return whatever you want.
>> Scott Moss: And we'll get into that in a second.
>> Scott Moss: So incoming query dictates what resolver's run and in what order. So again this query right here, myCat, because I have a query called myCat, that's the first resolver that's gonna run.
[00:05:26] If myCat, obviously has a name property, if I go to custom resolver for the name property, now that resolver is gonna run. If there was a property here for age resolver here for h, that resolver will also run in parallel with this one, cuz they're still blanks. So that's the order that those will always run in, cuz that's the way that the query was given.
[00:05:48] So it'll be just like that. But we're gonna go through that example in a minute.