Check out a free preview of the full REST & GraphQL API Design in Node.js, v2 (using Express & MongoDB) course:
The "Solution: GraphQL Schemas" Lesson is part of the full, REST & GraphQL API Design in Node.js, v2 (using Express & MongoDB) course featured in this preview video. Here's what you'd learn in this lesson:

Scott walks you through how to create the type definitions for Song and Playlist. He also answers questions about using comments in GraphQL and auto-generating schemas. - https://github.com/FrontendMasters/api-design-node-v2/tree/lesson-9-solution

Get Unlimited Access Now

Transcript from the "Solution: GraphQL Schemas" Lesson

[00:00:04]
>> Scott Moss: All right, so we're back. I hope everybody was able to create their type definitions for their song and playlist, based on what we did for the user. Again, there was no executing. There was no test. There was no pass or fail associated with this task. It was just to get you familiar with writing in a GraphQL file.

[00:00:20] Using the GraphQL syntax and starting off from our Mongo, or our Mongoose Schemas. So let's just look at what I have here, so you can go ahead and check out to the solution branch to follow along if you want. But let's see what I have for User. So you'll see some extra stuff here, this is not what we walked through, here's some extra stuff.

[00:00:39] I want to put this here so we can talk about it. It's easier for me to talk about it than have you do it without any context. So we have the type user we pretty much did this. I added two more fields to create it at or update it at.

[00:00:52] You might be thinking where did those come from because we haven't written any resolvers yet. But actually, those are gonna come from the database.
>> Scott Moss: I was gonna sneeze, nevermind. Wait, there it is.
>> Scott Moss: Okay, sorry. So the database has a timestamps true that we can pass in on the schema and that just puts timestamps on the database for us, the actual docs.

[00:01:16] So that's where I get those from. But if that was not there that's totally fine this is GraphQL. We just write a resolver and when we get to resolver it's gonna make sense. We will write a resolver that will tell graphic url when somebody is asking for the user type run this function that will resolve the data that was created.

[00:01:35] Like I said, resolvers are like mini controllers. We can write a function to resolve a data on any level, not just for the entire request, but for a type, for a field, for anything we can write a resolver for. And yeah, it's pretty cool. So that's another way we could have done it, but it's going to be automatically given to us.

[00:01:52] Then I have this thing right here called input I didn't talk about input, but anybody wanna take a guess what it is?
>> Speaker 2: A variable?
>> Scott Moss: I guess in some way it is a variable but I don't wanna confuse it with the variables you would pass in on a query.

[00:02:08] This is on a schema level so we're not actually using any data so I wouldn't say it's a variable in an effort not to confuse it with what's happening on the query side. But it does work like a variable. It's actually.
>> Speaker 2: The structure for the mutations?
>> Scott Moss: Yeah, so it's the structure.

[00:02:25] So when we did a mutation, remember we had a pass in of variable, and that variable had to match some type of input? That's what that is. This input is describing what the shape of the variable is. On any type of courier mutation, that's what it's saying. So we haven't used this input yet, but we will use it.

[00:02:45] So, basically, the short answer is an input is the same thing as a type, but can be used in arguments. It's the only thing that can be used in arguments. Whenever you use arguments, with couriers mutations, you can only use scholars or inputs. You cannot use types. Even if I made a input that is exactly the same as this type I still can't use this type in argument it'll break.

[00:03:10] You can't you can't use a type in argument it has to be an input. So and everything has to resolve to an input inside a graphical arguments doesn't matter how nested your structure is. Doesn't matter if you want an array of things, if it's not an input, it will break.

[00:03:26] And that sounds like, yeah, of course. But there's some times where you'll start writing queries. And you might have an input where it's like an array of users. And you'll put a user in that array. And it will be like, you can't do that. And they'll give you some obscure error and you won't know why.

[00:03:37] It's because something in there is not an input. But it is instead a type you need to make an input. Even if it's the exact same field you just gotta make it an input. So it's very strict in a good way. Any questions on this stuff?
>> Speaker 2: Can input contain a type?

[00:03:55]
>> Scott Moss: No it has to like for instance let's say I want this to have a user,. I couldn't use that because user is not an input, it's a type. So everything has to resolve to an input. So then I would have to go make another input to describe this, even though it could be the same thing as this.

[00:04:14] Yeah, it has to be. [LAUGH] Yeah. It's very strict. I read it. Like, when I was first learning this stuff, I think I was stuck on that for like three days. I was just like, I don't get it. I just don't get it, and it was that. So I wanna help you avoid that problem.

[00:04:29] Any other questions on this?
>> Speaker 2: We're using their tool to write the schema, right? To make it the syntax shorter? We're not doing it in the string long way, right?
>> Scott Moss: So, I guess, there's sort of three ways you could do it. So there's the JavaScript way, using GraphQL JS [COUGH] and you would use objects to create your scheme.

[00:04:53] You would do something like this, you'd come here and you're like I'm making a new GraphQL schemer We're not going to do that. The second way is that you just use a string inside of a JavaScript file. And that is cool but then you don't get syntax highlighting and you dont' get all this crap int he JavaScript file.

[00:05:11] This is like an abstraction on top of that method. So basically we're using a graphql file, which if you go look at the web pack configuration, I have a loader set up for GraphQL files that uses the raw loader, which basically turns it into a string. Raw loader is like a string, it's just a give me this file as string.

[00:05:31] So if I require this GraphQL file anywhere in my app, it's just going to read it as a string, which allows us to use that string method. So, yeah, that's pretty much what's happening there. So, Webpack's doing that for us. This allows us to import graphql files. But, again, we're on note so if we were not using Webpack and you still wanted to use graphql files on note, you could still do this, just use the file system to load it up.

[00:05:54] You could just use fs.
>> Scott Moss: Import fs from fs which is file system and then you can do file system.read file which allows you to read any file and read it as a stream. So you can totally do that as well if you're not using Webpack. On the client you couldn't do that, you would have to use a Webpack loader to use in a separate file cuz you can't read files in the client, right?

[00:06:17] That's a security thing. But yeah, good question, Webpack's doing that.
>> Scott Moss: Any other questions on this?
>> Speaker 2: How do you write a comment in GraphQL?
>> Scott Moss: That's such a good question. That's such a good question, so comments. So comments, they use the little hashtag thing, and you can put anything you want.

[00:06:40] But they have some significance. When you put a comment in GraphQL, that comment is going to be the documentation for the thing that you are commenting on inside a graphical. So if I were to put a comment on this ID right here and I'm like, this is the user ID.

[00:06:58] When I go to graphical and click on the user type, for its explanation it's gonna say that comment right there. So that's how you document your documents, it's really cool, very very useful we're actually using this extensively on my company to generate documentation for your documentation. It's actually really cool.

[00:07:17] So yeah, you can put that, pretty much anything you can put it above this whole type right here, and be like this is a user. And then when I go to graphical, it'll be like, what is a user? And then its definition like this is a user, you'll see that.

[00:07:30] And when we load graphical back up you'll see what I'm talking about. So, yeah, that's how you do comments. Use them extensively. All the stuff is generated for you. It's pretty awesome. Any other questions on this? Cool. Before I move on to the other files I just want to take note of this syntax just one more time and see how close it is to JSON but obviously not the same as JSON.

[00:07:55] It's not a validation but it also is the same. So notice there's really like no comments here. We don't need any comments here, there's no equal signs anywhere. But it's very similar to what you have adjacent so I just want you to take note of that and realize it's actually not as difficult as it may seem.

[00:08:11] It's just brand new. It's very similar to JSON, it's just, when I started learning this I remember thinking this is just, what? What is this? And then I was like wait, it's actually just kind of like JSON minus some stuff. So, it's not too bad. So if we go look at the song GraphQL, for my song type basically I have the ID.

[00:08:30] I think every type should have an ID on it, unless you're resolving a type from some source that doesn't have an ID. But, I can tell you when you start building out client apps with GraphQL, if you're using stuff like Apollo, they use the IDs by default to cache your graphical data type.

[00:08:50] If you don't have something unique about that, it can pretty much skip your cache every time, and you kind of miss on that. So, I would say probably always use IDs if you can. But sometimes you might be resolving data for something that doesn't have an ID. Right now we're resolving data from my database which always has an ID.

[00:09:05] But maybe you're resolving from some thing somewhere else, I don't know. Because you could resolve from anywhere and maybe those things don't have IDs. You should still probably find some to unique identifier for the thing you're resolving, to have caching on your front end. So I have a ID here, title, URL, album, artist, rating favorite, exactly the same as the model that we created, with the same restrictions.

[00:09:34]
>> Scott Moss: Does everybody have something similar to this? All right, then I also have two inputs here. So we're gonna get into inputs. Again, but remember, inputs are literally just types that we can use in arguments. That is the only thing that's special about them. They're the same thing as types that you can use in arguments.

[00:09:50] But I wanna walk through why this input looks very similar to the type song, but slightly different in the naming of it. So I have an input called updated song. And the reason I call it updated song because this is the variable name, this is the input type that you'll have to use when you want to update a song, right?

[00:10:09] So if you just glance over the fields, there are literally the exactly the same as the type of song. But remember inputs can only be used for variables, I'm sorry inputs. So we had to make another one. And the other difference is there's not requirements on any of this stuff.

[00:10:26] Why is that? Because I put requirements on these. That means when you do an update you have to update all this stuff. I don't want you to do that. The only requirement I need is the ID to know what you're trying to update and then, you pass in the field that you want to update on.

[00:10:41] I don't know what you're gonna update. Maybe you're only gonna update the title. Maybe you're only updating the artist, I don't know. So I'm not going to require any of them. I'm only gonna require the one thing that I do need, and that's the ID to find the thing that you're trying to update.

[00:10:52] Everything else is not required. Does that make sense? All right. And then on a new song, some of these should have requirement on them because some need a title. So that should be required. And a favorite, it also needs a favorite. Has a default on it and so it's totally fine.

[00:11:17] We don't have to do anything
>> Scott Moss: The other special thing about inputs that you can't do on type is that you can set defaults on them. So notice we have a default on favorite here on the mongoose model on an input. And only on an input. You can also set defaults.

[00:11:37] So I can say for this favorite one I also want to set the default to false. Like I said, equals false, like that. This only works on inputs, doesn't work on types. So if you try to do this on a type, it'll be like nah, not gonna happen.

[00:11:54] I hardly ever use these. I don't, for some reason I just don't like doing this on this level. I prefer doing database level. So use them if you want to but I prefer not to use default GraphQL. Because it's like what if your data is accessed some other mechanism other than GraphQL.

[00:12:15] What if you have to do some quick data base insertion on the terminal. And then you don't benefit from those defaults because you just bypassed GraphQL. So I like to do it closer to the source of the data, which is the closest you can get at the database.

[00:12:27] So I'd rather do it there.
>> Scott Moss: Then again, you might be doing mutations on something that has nothing to do with storing data somewhere. Maybe that's the case, I'm not sure. So that's song, and if we go to playlist, playlist, let's pull up the model here, playlist.model. This one's pretty simple, so we have a type, we have an ID, we have a title, and then we have songs.

[00:12:55] So this is one I said I didn't really tell you about, but if you did some looking probably figured it out, I saw some people figure it out. Let's talk about this. So, we have a song here, we know in Mongo that those songs are going to reference an object ID, right?

[00:13:08] An object ID is really a string. This is a string that references a song. So viewed, literally just did a one for one match. You might have came in here and you might of said, there ID's,.right? Because these are IDs in here. And you would not be wrong if you did that.

[00:13:27] The only problem is you're probably not gonna to get the desired result that you're looking for. You're not gonna be able to query this playlist and then gimme all of the songs on this playlist, and then I want the titles for these songs. That wouldn't work. When you ask for songs on playlist it give you in an array of strings, an array of IDs.

[00:13:43] Which is probably not what you want you probably want back an array of songs so what you would do you would use the song type that you created over here. There are in two separate files so how do they know about each other. Well when we join them together, grab the thing that we're gonna be using, the tool, is gonna build the whole scheme up first and then it's gonna execute it.

[00:14:03] So it's all in the same context before it's executed. So it's still gonna be fine. It doesn't matter what order it was in, which was first, which was last. The whole thing gets loaded up and then it executes. So, this will know about the sing when we run the query.

[00:14:18] So by doing this now, it's going to be our resolvers job to guarantee when someone asks for a song that they don't get back in the array of ID's. Instead they get back an array of objects. So remember yesterday I talked about population, how mongoose can populate things.

[00:14:36] Like join tables on the fly, that's what we're gonna have to do for the songs array. We're gonna say man, somebody asked for a song on the playlist. Now there's IDs. Can we populate those IDs to be actual song objects? And make sure those song objects match this data structure because that's what it resolves to?

[00:14:55] It sounds crazy right now but I promise you gonna be so powerful, you're gonna like what? How do we do that? It's gonna be really cool. So that's how that works and you'll see. It's very, very powerful and the way you do it is very awesome in my opinion.

[00:15:10] Any questions on that? Yes.
>> Speaker 2: So [COUGH]
>> Scott Moss: There's a lot of, kinda boilerplate, very similar code. And I know you mentioned at the outset that you can do dynamic schema generation. And that's more complex.
>> Speaker 2: Yes.
>> Scott Moss: Would that fall under what I've described here? Or is that a little bit, I don't how to describe it.

[00:15:31] Ultimately, what I'm trying to drive at is that instead of having to write
>> Speaker 2: Updated song and new song inputs for every type.
>> Scott Moss: Right.
>> Speaker 2: Is there a way to do that so you don't have to.
>> Scott Moss: Yeah, there is. Yes. In a short answer, yes. It depends on.

[00:15:49] So there are some plugins that will literally read your Mongoose schemas and generate everything for you, because it's like a one for one. So we can use those. I don't wanna do them. You can also do it yourself, because our API is so simple, you can totally do it.

[00:16:03] The way I would do it is, I would just have a file like this, that just has like a function, that like takes in like type, and then it just generates everything. Because it's all the same, they all have the same queries, the same mutations and it just generates a type.

[00:16:20] So you can totally do that but yeah we're not gonna get to that. Because there's so much you can do with that, and so many edge cases. But yeah, that's the beauty of GraphQL is that you can do that per request. So whenever a request comes in you can say, this user is an admin, this admin needs this schema.

[00:16:38] Let me generate this schema. And then because you generated new schema you also generate those resolvers too. And resolvers are functions they are not strings. So that's like a whole nother thing right there. So then you you gotta generate those resolvers, very similar to how we generated controllers yesterday, but on the fly, then build your schema.

[00:16:55] And then run the query against that schema. So you could totally do it, and then later on today I could show you how I'm doing it with the product that I'm working on cuz we're doing the exact same thing but on a whole other level. But yeah, you could totally do that and you can't really do that with rest.

[00:17:12] You need a v2 for that, [LAUGH] or v3, or whatever. And then for the input very similar to the type I think the title yeah title should be required. You can pass in new songs here if you want. And then favorite so it's a boolean. Any questions on these stuff?

[00:17:36]
>> Speaker 2: So there because it's an input you would be
>> Scott Moss: Required to update the title anytime you update.
>> Speaker 2: Yeah. Actually, that's right. That's an update. I thought it was a new input. That's an updated playlist. You're right. It does need to be required on an update. I thought that was new, it's updated.

[00:17:50] On a new playlist input, you totally would need it. On an updated one, no, you just give me id and whatever else you pass I'll just figure out. We'll write the resolver for this, so you'll see it's pretty awesome. Any questions on this before we move one. All right, As you can tell I'm really excited about GraphQL.

[00:18:09] I've literally been working on GraphQL like the advanced part of it for the last eight weeks and it's all I know so I'm really excited to talk about this stuff and I'm like holding back from going to crazy mode. [LAUGH]