Server-Side GraphQL in Node.js

Query Types Solution

Scott Moss

Scott Moss

Superfilter AI
Server-Side GraphQL in Node.js

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

The "Query Types Solution" Lesson is part of the full, Server-Side GraphQL in Node.js course featured in this preview video. Here's what you'd learn in this lesson:

Scott live codes the solution to the query types exercise.


Transcript from the "Query Types Solution" Lesson

>> Scott Moss: All right, so now we're back. Hopefully, everyone had enough time to get through that exercise. If you ran through an issue where, I'm sorry, if the app was breaking, well, not breaking. But it wasn't returning a context like it should have been. I discovered there might be an issue with the file syncing of the database in the different versions of Node.

Or possibly something else, but it's failing silently. So that's the thing, we had one person with that problem. So if you're having that problem, I will get to the bottom of it soon. If not, I'll offer an alternative with just using something in memory versus using a file, which is a lot simpler.

But let's hop into the solution here. So, where are we at? Let's go to schema. So first thing is, if you look at this schema and then you look at schema.json, open that up here. Open this one up here. We wanna make some types based off these schemas inside of schema.json.

So I'll do just that. So I'll have a type User that has an id. And I'm gonna give it the ID type. Very misleading, I put string here. But this is where you would use the ID type. Which like I said, is basically a string, but it's meant to be a unique identifier.

That's it. I don't think GraphQL does anything. GraphQL doesn't know if this is unique. It's not gonna know that this user's id is different than this. It's not doing that check. This is more meant for you as a developer to represent this as being a unique identifier. As far as GraphQL is concerned, I'm pretty sure this gets parsed exactly the same way a string gets parsed.

So that's that. And that's always gonna be there, so I'll put a bang. Also has a username, that's gonna be a String, and also gonna say that that's there. So that's it for the User, for now. And one thing to note is, this is what your schema will look like in your database.

And the reason I'm saying that is cuz as we get further along, we're gonna be adding more fields to the types that don't exist in the database. So you can think of them as virtual fields. So I just want you to think about it, it's not always one-for-one.

You might start off with your GraphQL types looking like your models and stuff like that. But they will most, eventually expand to be something else. Just like if you ever used any ORM that allows you to virtualize fields that don't actually persist in the database, it's basically the same thing.

You can virtualize fields, and that represents a combination of things or something like that. And we got type Pet, which has also an ID, also has a createdAt. createdAt is a timestamp. I just found out that you can't actually use the Int one here, cuz of the bits.

So, and Flow obviously won't work. So I'm just gonna put String here and use a string timestamp. And that's always gonna be there. The other alternative is you could create, or download and install a new scalar just for dates. I'm pretty sure there's a lot of them that I've seen.

If you look, you'll find a lot of them. But I don't wanna get into custom scalars right now, because that's a whole nother thing. But yeah, you can add custom scalars, which are very primitive values. The way you do that is, like I said, you either install it or you have to create the parsing mechanisms, how they come into the schema, how they go out of the schema, all that stuff, the validation rules.

It's a lot that goes into creating a scalar. So that's why we're not gonna get into it, but you could do that. The name is a String, and it's always gonna be there. And type is a String, and that's always gonna be there. So that's it for the types, as far as the models in our database go.

Okay, the next thing is we need to create a query to get all the pets. So I'm gonna say, type Query. And I'm gonna call it pets. And for now, it's just gonna return all the pets in the database. And the array has to be there.
>> Scott Moss: And that's it for Query.

>> Scott Moss: Any questions on the Query type?
>> Scott Moss: Yes?
>> Speaker 2: The case of pets, lowercase versus uppercase [INAUDIBLE]?
>> Scott Moss: So you're saying what's the difference between this lowercase pets field and this uppercase Pet type?
>> Speaker 2: Yeah.
>> Scott Moss: Right, so they have no relation. I can call this whatever I want, doesn't matter.

This field, its name has no relation as far as GraphQL is concerned, with its value. This is just for us, this is just my convention of querying a list of things. And usually in GraphQL, when I'm like, all right, I need to query a list of things, I'll take whatever that thing is I'm querying for, lowercase it and pluralize it, and say that's what I'm querying for.

So if this was users, I would have called this users, and this would have been User. But some people have their own convention. Some people might say something like petsList or allPets. So it's up to you. But for me, that's simple for me cuz I always did it that way.

>> Speaker 2: But the key, the casing and whatever it is has to match whatever is on the resolver?
>> Scott Moss: 100%.
>> Speaker 2: Okay.
>> Scott Moss: So yeah, that's why the validations on what you can put here are very similar to the validations to what you can put in a JavaScript object.

So you couldn't come in here and say, this is a field. That's not gonna work. Cuz you can't put that an object, at least not without putting a bracket and having it evaluate it. So it has to be a valid property name. Therefore, your resolvers can match up with it.

>> Scott Moss: Yes?
>> Speaker 3: So one thing I saw was that while defining a type, it was colliding with the type user in demo.js. So is there a way to namespace these, or are these always global?
>> Scott Moss: So that conflict was probably just a tool that you had built into your IDE.

It shouldn't conflict our runtime, unless for some reason, you were importing those typeDefs into the same server, right? Because that server that's inside of this repo is different than the server that I created in the demo file. These are two different servers, so they don't know about each other.

So the only conflict you should have had is probably some tooling in your editor that was like, hey, there's already a thing here. And at runtime, you can ignore that, cuz it's not gonna matter anymore.
>> Speaker 4: I was just gonna say, I was running into that error too.

But then when I commented out Pet and users on the resolvers, I didn't have that issue. And I didn't need to comment out my demo.js file.
>> Scott Moss: Say that again, you commented out what?
>> Speaker 4: So I was having issues too, something about the users model or whatever. But I think it had to do with the resolvers.js file, and not the demo.js file.

>> Scott Moss: Got it, okay.
>> Speaker 4: So as soon as I commented it out on the resolvers in this directory, I didn't have the error anymore.
>> Scott Moss: Yeah, so if you had an error at runtime, the server printed out an error, then yeah, there's something wrong with your server. But if you had an error, as in just browsing your code and it's saying this is broken, then that's a plugin, yeah.

But yeah, you're right. We're gonna come into that error too. That error is because I have placeholders in the resolvers file for code that you're gonna be writing. But there's no code there yet, so it breaks. So I left that there intentionally, so you can see the output.

And you can figure out that, I gotta get rid of this. Which I'm glad you did, so that's great, yeah. Any other questions?
>> Scott Moss: No? Okay, so now we'll go to resolvers. And let's just try to run this and see what happens. So if I go here, the command I wanna run will be, if you look at that package, that JSON, there's one for yarn server.

Or you can just type in node and follow the path to the server, but you can just run yarn server.
>> Scott Moss: And this breaks, right? So this breaks for a couple of reasons. One, I never even created a server. The resolvers and stuff are messed up. So this is kinda like how I develop.

I like just error-driven development. I'll just let it break, [LAUGH] hoping the errors will tell me what to do. So it's really good. To me, it's better than TDD, [LAUGH] at least initially. Initially, at least, okay? So now we'll go to the server. And we'll make this a server.

So to make this a server, we're just gonna pass in some typeDefs and some resolvers here. And then I'm going to do some context like this. And I'm just gonna return my models, cuz I like to use my models in here. I'm also just gonna add the db, just in case.

You wanna do some raw db stuff that's not included on the models, you can do that. I mean, you can put whatever you want here. So I got that, so now we're gonna try to run it. Let's see,
>> Scott Moss: Get another error. So this error is basically saying you defined a mutation in the resolvers, but not in the schema.

So that's the errors that you probably would have ran into. So the way you would have to get past that is, you'd have to go back to resolvers, and basically just start commenting out any resolvers that aren't associated in the schema. That includes this User one, I'm gonna comment that out too.

So now if we run this, we get another error. So pet.img is defined in resolvers, but not in the schema. So you might as well just go ahead and get rid of this Pet too. Or you can just get rid of that image, it's up to you. I'm just gonna get rid of the whole Pet.

And boom, at least we're error free. The server's running. But we're not quite done yet, because we haven't defined a query for pets. So to do that, I go to the Query field in the resolvers. I type in pets exactly the way it is here. Just copy and paste it, to be safe.

And that's a function. The initial value is nothing. We don't have any arguments, so that's nothing. But we do have the context here. So I'm gonna get that. There is a fourth argument here. I didn't talk about it because it's really advanced and you will probably never use it.

But it's basically the AST of the incoming query.
>> Scott Moss: Yeah, that's basically what it is. I think it also has the schema on it as well. So this is useful for doing projection on database queries. So if you wanna only query for the specific, if your GraphQL types match up one-for-one with your database types, you could theoretically just look at that info object, look at every field that's coming here from the query, and go to your database and only query for those fields.

So you can optimize your database queries that way. And that's what the info object would be for. But that only works if there's a one-to-one relationship with the fields and your GraphQL schema, versus the fields on your models and your database. Then you can do something like that.

And there are tons of libraries that do that for different types of databases if you're interested. But in my opinion, probably not. You're probably gonna have way more stuff over here than you do here. So a one-to-one projection is probably not gonna happen. At first, it will. It's gonna seem legit.

But then eventually, you're gonna get real serious about GraphQL and it's just not gonna happen. So now we got the context, and we should be able to just return ctx.models.Pet.find,
>> Scott Moss: Many. Type was butterfly thing, okay? All right, so now we'll restart this.
>> Scott Moss: And we'll go to Playground.

>> Scott Moss: Refresh that, and verify I'm looking at our docs. Cool, we got Pets. If you click on it, you can see a Pet has an ID. You click on this, it'll even tell you, the ID scalar type represents a unique. It breaks it down so hard for you, you just can't get this wrong.

All right, so now we can query for our pets. And I should be able to get my pet stuff, like this. And hit Enter, and I'll just get back at the end of the array. So I saw a lot of people that tried to do this. Cuz they were like, there's no pets in there.

So I'll just do this. But remember, GraphQL doesn't know what to do. You've gotta think about it like this. It's like if you had a baby that had superpowers. It can do anything, but only if you told it what to do, cuz it just doesn't understand much. You have to tell it exactly what to do, and then it can do it.

We're not telling it what to do. We're just saying, pets. And it's like, cool, but what about the pets? We have to tell it. Otherwise, it won't know what resolvers to go to. It won't know what to execute. It can do it, but you have to be very specific and explicit about it, so.

And because pets is an object type and has sub-fields, that's what this is saying. It's saying, you must have a selection of sub-fields. Did you mean pets, this bracket with some fields in here? Is that what you meant? Because I'm pretty sure Pet is an object. And you didn't ask for any field, so I don't know what you want me to do.

That's what it's saying. So you have to do that. Even though you don't have any pets, you can do that and it works.

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