Server-Side GraphQL in Next.js

Resolvers Q&A

Scott Moss

Scott Moss

Superfilter AI
Server-Side GraphQL in Next.js

Check out a free preview of the full Server-Side GraphQL in Next.js course

The "Resolvers Q&A" Lesson is part of the full, Server-Side GraphQL in Next.js course featured in this preview video. Here's what you'd learn in this lesson:

Scott answers a student's question regarding how to pass additional data to the context object in GraphQL resolvers. He also discusses the concept of the parent argument in resolver functions and explains how to determine the parent based on the query structure.


Transcript from the "Resolvers Q&A" Lesson

>> Participant: Can we pass anything to context? For example, can we pass the drizzle instance in the context?
>> Scott Moss: Absolutely, you can pass whatever you want to the context. So if you go to the route, you can put anything here. Doesn't matter what you put here. You can literally put whatever you want here, and it'll be on the context object.

The third argument inside of every single resolver, as far as putting drizzle here. Sure, db I can grab the database from drizzle and I can put it here. And in fact, this is usually recommended, the convention is, if you're in a resolver, look at all the dependencies of a resolver.

You can see I have this dependency on the database, right? But it's not coming from the argument, so this is kind of a dependency injection pattern. If you ever use angular or anything like that, there's the concept of dependency injection, which basically makes your functions really easy to test.

Because if every dependency that it needs is an argument, when you go run this function in any other context, you can mock those things out easily by passing in whatever you want as an argument. So if you really wanna test these very flexible, then make sure all of the dependencies are arguments.

So you can't control this, you can only control this through the schema. The only thing you do have control over is the context object. So in this case, yes, passing the database onto the context. So then you could do something like, ctx.db, yeah, that would be super flexible.

The reason I didn't do it is because check this out, It just says any now. I lose all the benefit of drizzle having those types because I didn't do the GraphQL code gen to map this database to the output of drizzle, it's just crazy TypeScript work. So I decided not to do it, so I could still get this.

I still like this TypeScript here. So that's the only reason I didn't do it, but yeah, in a production app, I would just do the work, map the types, and add it to my context object. Every single thing that my resolver needed would be on the context here because when I go and test it I can change it.

I can mock it, I can do whatever I want versus right now I'm creating, somewhat of a closure by pulling it from the top here, which makes this very hard to test. Then you got to have a testing framework that can mock out a whole module, its just a lot of work.

One thing I just wanna kind of double down on cuz I realized this was very complicated for me, it's just the concept of this first argument, which is the parent argument. I think what's complicated about this is, when you think about how the resolvers work, it's easy to think that the resolvers are routes that run when something happen but that's actually not the case.

I can look at a GraphQL query and tell you what resolver is going to run just based off the query, right? So if I go here, if I look at this query I can tell you what resolver is gonna run. The first resolver that's gonna run is gonna be the issues resolver on the query object.

That's gonna run, it's gonna return this whole thing, okay? The next resolver that's going to run, is going to be the user resolver on the issue type. How do I know that? Well, because issues returns an array of issues. So for each one of those issues, I want to get the user.

So that means this user is on the issue type, so I know that the issue resolver on the user type in the resolver is gonna run, right? So if I go here, that's what's gonna run. And then what's gonna run next? The user is gonna return those fields and then the issues field on the user type is going to run, right?

So if I go to the user type, the issues field is gonna run. And I know that just by looking at the query. So if you wanna know what the parent means, you can just look at your query. When this user resolver runs on the issue type, what is above it?

The issue, so that's the parent. The parent is the issue here. It's the one that's above it, the one it's inside of. Okay, when the issues resolver runs on the user type, so when this function runs, what's its parent? The user, the user is the parent. That's what's above it, so that's how you can figure out what the parent is.

And this is why I said, you'll never have a parent on top level queries and mutations because what is above it? Nothing, there's nothing above it. It's the first thing you're running, so there's never parent. But these nested ones, they're called nested because they're inside of something else.

So the thing that they're inside of is its parent. So just by looking at this query, I know that the parent is issue. I know that this parent is user.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now