Client-Side GraphQL with React, v2

Nested Data Queries

Scott Moss

Scott Moss

Superfilter AI
Client-Side GraphQL with React, v2

Check out a free preview of the full Client-Side GraphQL with React, v2 course

The "Nested Data Queries" Lesson is part of the full, Client-Side GraphQL with React, v2 course featured in this preview video. Here's what you'd learn in this lesson:

Scott demonstrates how to make a query to retrieve issues from a GraphQL server using Apollo Studio to explore the schema and write the query. He also explains the concept of nested queries and the potential performance issues that can arise from recursive queries. Scott then shows an example of a recursive query and mentions some potential solutions to handle these performance issues.


Transcript from the "Nested Data Queries" Lesson

>> Speaker 1: What we wanna look at next is gonna be the issues query. So we wanna be able to pull down any issues, and so that they can actually show up on a page, right? So if I go to my app, it's not gonna show anything. One, cuz I don't have any issues, but also, two, it's not really making a query.

We don't really have that hooked up. So let me go check. Yeah, we're not doing anything, so we actually have to hook this up. So we need to make the query and we need to pull them down. And even then we won't actually get anything because you don't have any in the database.

You won't see anything until you actually create the issue. So let's go ahead and make the query that gets the issues. So again, I'm gonna do the same thing I always do. I'm gonna go into the Apollo studio, I'm gonna use the tool there to explore and help me write the query cuz I get the type checks, I get all that stuff there.

You could set all that stuff up in your editor I talked about a little earlier using code generation to generate typescript types. That'll give you autocomplete with your query schema and all that stuff, but I'll just use a studio, which is better, in my opinion. So I'm just gonna do that.

Also, I'm, I guess, a little hardheaded, or I'm just anti anything Codegen. I think it's just not the best way [LAUGH] to get what you need, but sometimes that's the only way. And so I like TRPC cuz you don't have to do Codegen, but at the same time I like the query language of GraphQL.

So that's just how it is. But yeah, let's go here, let's go to Apollo server. I'm gonna just get rid of this, I'm going to click on Query. I'm gonna click on the issues query.
>> Speaker 1: And then you can see all the fields that issue has. I'm just gonna get rid of these variables for now.

So I have a bunch of stuff here. So one, I can get the content, that's the actual body, the issue, the createdAt, the ID, the name, status. And then I'm gonna talk about this, the user in a little bit, because that one's kinda special. And then as far as the arguments go, it looks like I have a null argument.

So it can be null. So I don't have to supply this argument, it's not not-null. But if I were, it looks like it's an array of statuses. So it's an array of issueStatus. But look at issueStatus, you can see it's actually a enum of one of these values.

So backlog, todo, inprogress, done. It could be one of those. But we don't have to supply it, it's optional. So I'm gonna run this. I should just get back to empty array cuz I don't have any issues for this user. Or maybe I do have some issues for this user, I guess.

Yeah, I did actually sign up to make some for this user. Okay, or whatever user is actually signed in here has issues. I would have to make a new user for my database that doesn't have issues. But I guess this is great cuz I can actually show you some things.

So this is perfect. So let's take a look at this. So with the issues here, obviously we're getting back an array of objects with the properties on them that I asked for. But then also, if we go and look at the input filter, so if I click on the input filter here, you can see I have statuses.

So I'm gonna add this, I'm gonna say statuses. And then I want to, I'm gonna click on this, I wanna get all the ones, let me see, are all these backlog? I think they're all backlog, although I have it inprogress, inprogress. Okay, this will work then. So I'm gonna get all the ones that are, say, inprogress.

So I'll say statuses, and then I'll say inprogress. So if I do this, if my database query works, I should only get back the issues whose statuses are inprogress. So if I run this, I definitely only get back the statuses that are inprogress. So you won't see this as you probably don't have any issues in your database.

But that's how that filtering works. So it's quite powerful on how you can do that. And then when it comes to relational data, how you would do that and rest. How would you invest, say, give me all the issues, and then also join this table on the issues and give me this relational data?

I don't know how you would do that and rest, that kinda defeats the purpose. Rest will just be another API call. Technically, you'd have to get the ID of the joint table resource that you want, map it to another rest call for that resource. Or again, some developer created a query language that lets you do it in one call, which is kinda sloppy.

But here in GraphQL, if I wanted to get the user for all these issues, I could just come down here. And if I go look at a schema, you can see that issue does have a user whose type is user, and it's non-null. So it's guaranteed to be there.

And that user has these fields on them, an ID. It also has issues. I'm gonna demonstrate something that's gonna be, do I have that hooked up on the backend? We're gonna see. We're gonna see something interesting here. And if you're already seeing what I'm thinking, then you're probably curious what's gonna happen here.

But let's see, so I'm gonna say, I'm gonna get the user, right? And then I wanna get the ID of the user, so let's just run that.
>> Speaker 1: Cool, so I got the user, and I got the ID of the user. Let me close this.
>> Speaker 1: Give this some more space.

There we go, I'll just do that. So we got the user, and we got the ID of the user for every single size, and it's the same user. So they all have the same ID. But now because a user can also have issues, and an issue can have a user but get into this cyclic scenario where it's recursive, right, it's cyclic.

So now if I go in here and I say, I wanna get the issues for this user. All right, I could do that and I can get the names and the status. So if you look at the query I'm doing, I'm saying, give me all the issues, give me the user for those issues, and then give me the issues for that user.

So if I run this, look what I got, I got the issues, and I got the users for the issues, and then I got the issues for the user, and it resolved them. And then I could say, give me the user for these issues, for the user for these issues.

And I could run that. And what do you know? That works as well. This might show you how powerful GraphQL is, but it also might make you scared about how you have this N+1 problem on the backend where this is recursive in nature, like where does it end?

If this was a developer API, like a public-facing API or private, if it was private but it was consumed by developers. And they could, I mean, they could basically DDoS you, I guess, if they wanted to. [LAUGH] Somebody could just write a really, really long script, a really long query, just keep it going and then, how does your server respond to that?

How does it know when to stop? There is no one way to get past that. There are many solutions with trade-offs to help you get past it, but it's definitely not like. You still have to write your GraphQL server in a way that it can resolve this query, and then you add protections on top of it to prevent users from doing things like this, cuz I could just keep going cuz it's cool.

Give me the issues, give me the user, give me the issues, give me the user, give me the issues, give me the user, and then give me the ID. I could just do that, and then let's run it. You can see it's taking a lot longer now. I don't know if this is even gonna resolve, or if it does.

[LAUGH] Yeah, you can see I just broke my own server pretty much trying to do that. Let me see if it actually is broken. I think it's trying. Well, you see, that's a huge problem, right? So some of the solutions that some people do is, I've seen people basically give certain fields weights like scores.

And then what they can do is, every time there's a new resolver running, they can count the score so far and they give the query a maximum score of, you cannot go over this score. If that's the case, you just did too much retrieval. So it won't allow that to happen.

So sometimes I'll see that. There's other ways where you can tap into the AST of the GraphQL and determine how many levels deep they might be right now. And then you can try to resolve it that way. There's a lot of ways to get past it, but like I said, there isn't just one way.

So it can be pretty fragile, but at the same time you can see just how, there you go. It finally timed out. I was gonna say, did it do it? No, it finally timed out. So it gave up on trying to make that dream happen. So pretty impressive there.

Any questions?
>> Speaker 1: And I think the thing that's important to remember here is, no one wrote. No one explicitly wrote the queries for that, this is dynamic. I'm querying that myself and the server just has to respond to it. So it has to be dynamic in how it handles queries.

So it wasn't written ahead of time, which is something you would have to do with any other protocol. You have to write it ahead of time. And I think that's the power of using GraphQL on the frontend is that you can be dynamic with your queries and the things that you ask for.

Although in practice what tends to happen is, for the queries, one of the big reasons people have these names here on these queries like this is so that they can have these name queries in which they, I forgot the word that they would call it. The airman has different words for it, but it's basically a saved query, whereas I can register a query on the backend cuz I'm never gonna change it, right?

Once I forgot my queries from my app, I'm never gonna change them again unless it's time to. They're not gonna be dynamic as if a developer was hitting my API, and is changing every time. But if it's for my app, I know what the query is, I wrote it.

So therefore, I can just save that query on the backend somewhere, and then I can just get an ID to reference it. So this is query one, this is query two, this is query three. So then I can just go to my API and like, give me the results for query one, here are the variables.

Give me the results for query two, here are the variables. So there's people who do stuff in their build pipeline where they will register their queries and save them. And that's kinda saves on the step on the backend where your GraphQL server needs to validate your query against a schema.

It bypasses all of that because you validated it when you saved it the first time. So it kinda speeds things up and it saves your queries, but it's like a performance thing that some people do. But again, you wouldn't do that if you had a GraphQL API that was developer-facing.

That would only be for, I know what queries I'm writing because this is for my app. Is this called nested queries or recursive queries? I don't think it has official term, I've always referred to it as N+1 queries.
>> Speaker 1: Pretty sure that's
>> Speaker 1: Yeah, the N+1 query problem performance issue, yeah.

That's basically how I think the community refers to it as. So anytime you go more than one level deep, and N is like, how many levels are you going? You just don't know. So I think that's what it's actually called. But yeah, you are getting nested data for sure.

It's basically like cyclic data. If you are referencing a node that can reference it's parent's node, then it's like, if you try to log out with something circular in JavaScript, can't even think about it. But if you had a circular object and you tried to log that out, or if you try to JSON stringify it, it won't work.

Because the way Jason stringify works, it recursively goes through everything, and it tries to turn into a string. But if it's recursive, if it's cyclic, it can't, it doesn't know when to stop. So you'll get a error saying, cannot stringify, this object is cyclic in nature. It's the same problem, it doesn't know when to stop.

And because now you're not just playing with some one CPU, stringifying it, you're playing with database resources or whatever your data source is, it's quite expensive. As you can see, I literally killed my server on just that one call, imagine a team of people doing that to your server.

It would come crashing down instantly, so.

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