Server-Side GraphQL in Node.js Mutation Type
Transcript from the "Mutation Type" Lesson
>> Scott Moss: So what are mutations? It's basically a type on a schema that defines operations clients can perform to mutate data, which basically means create, update, and delete. So queries are types on the schema that define operations that a client can retrieve data. A mutation is the opposite, it's basically you're modifying, the intention is you're modifying something.
[00:00:19] Now, GraphQL can't enforce what you do inside of a resolver. You can do whatever you want. You could make a mutation that doesn't modify anything, and just send something down, like a query does. But why would you do that? I don't know. There's no enforcement, like it doesn't know what you're doing in the resolver.
[00:00:34] That's the intention. The intention is you are modifying something. If you're not, then it's probably a query. There's also a third thing called a subscription. But we're not talking about subscriptions in this course, that's for the advanced course. But most of the time it's either a query or a mutation.
[00:00:49] So mutations are for when you want to create, update, or delete. So if you're doing REST, this will be post, put, and delete. That's what this is.
>> Scott Moss: So how do you create mutations? Well, you define a mutation type on the schema using the schema definition language, just like we did with queries and every other type.
[00:01:06] You add fields for the mutation type, just like we did with queries and every other type. You add arguments for mutations. So most mutations will probably have arguments. If you're creating something, deleting something, or updating something, you'll probably have an argument. Otherwise, how do you know what you are creating, how do you know what you're deleting, how do you know what you're updating?
[00:01:28] So most of the time, your mutations are gonna have arguments, unless there's just this one thing on the server, and all you gotta do is just hit it with no arguments to, I don't know, delete it maybe? I don't know, you will probably always need arguments for mutations.
[00:01:42] I can't think of a use case where you wouldn't. So you probably always have them. You don't need them. It's not a requirement. I'm just logically saying, you will probably always have arguments there. Whereas quarters you can get away with just saying give me everything. I don't need an argument.
[00:01:56] And then the last thing is you need to create for the resolvers for the mutation fields, just like you do with all of the other types, right. So let's talk about that.
>> Scott Moss: Let me go to that demo thing one more time. Here we go. So if we were to, let me close a lot of this stuff, a lot of noise.
>> Scott Moss: All right, so we're gonna make a Mutation here. And again, a Mutation is not required, only thing that's required in a schema is a Query. You just say type Mutation, just like this. And just like Query, GraphQL is looking for a type called Mutation for its Mutation type.
[00:02:34] So if you want to call it something else, you have to tell GraphQL, my mutation type is actually called something else. But again, I do not recommend doing that. For the same reasons I don't recommend doing it for the query. So we'll just call it mutation and just stick with that.
[00:02:47] And it's the same thing as a query. Nothing special here. So I'm this case, if I wanted to create a new shoe, I would say, cool, I'm gonna make a mutation called shoe, or new shoe, or whatever you want, create shoe. It's gonna take an argument. It's gonna take some new shoe input, that's required.
[00:03:09] You can't create a new shoe unless you give me an argument to create one. So I'm gonna make it required, there's nothing I can do if you didn't pass me that argument. And then it's going to return, you guessed it, a shoe, that's always gonna be there. So if this resolver doesn't error out, you are guaranteed to get a shoe.
[00:03:26] If it does error out, you're gonna see the error. So that's basically it. So now you have to define your input type. So your input type for a new shoe input. And all new shoes have a brand which is required, string. And a size. Looks like I already have shoes inputs.
[00:03:47] So actually I can't use shoes input, because these aren't required. These are optional and I need these to be required. So I have to make a whole new one. Even though they are the exact same fields, I need these to be required on creation, but optional on query.
>> Speaker 2: If you had a complex input, could you extend it, and then rewrite the required fields?
>> Scott Moss: I wish. You can't. I think I actually made an issue on GitHub about two years ago to do that.
>> Speaker 2: They haven't listened to you yet.
>> Scott Moss: We talked about it.
[00:04:16] We talked about it. But no you can't.
>> Speaker 2: All right.
>> Scott Moss: Cool, so now we've got our new shoe input, got the new shoe here. And then resolvers. Anyone know the name of the resolver that I have to put here?
>> Speaker 2: Mutation?
>> Scott Moss: Exactly. Mutation cuz that's the type.
[00:04:31] Just like I did with query. And then the actual name of the mutation, which is NewShoe. This command button is so jammed. newShoe. And first argument, nothing here. Arguments from the client, got the input. And then you have your context and stuff like that. So then over here, you just create a new shoe.
[00:04:52] I don't have any data or anything like that. So I'm just gonna return the input here and just call it a day. But that's it. That's how you would create a mutation. So if I were to start this.
>> Speaker 2: Do names under queries and mutations conflict?
>> Scott Moss: So if you had a mutation with the same name-
>> Speaker 2: Called shoe?
>> Scott Moss: No, they don't conflict. Because the only thing that conflicts. So when you're inside of a type, the only thing that conflicts is if you had a field with the exact same name. But field names are scoped by the type that they belong to, so they don't bleed out.
[00:05:25] So you could theoretically have ten different types in here, and they all have the exact same field name, and that would not be a problem. Cool, so if I go here, refresh this thing. Back in the demo thing now. Get rid of these. If I look at my docks, I should have, you notice this new section here, mutations.
[00:05:43] It's like they knew it was gonna happen. So this is what I was saying, GraphQL treats queries and mutations differently. You don't see a section here for every type that we have. You only see a section here for queries and a section here for mutations, cuz that's built into the GraphQL spec, that's a special thing.
[00:06:00] It just so happens we treat them as regular types. So don't override them unless you have a good reason to. Let me see. Cool, so we see this new mutation we have here for Shoe. And what we're gonna do is go ahead and run that mutation. The way you do that is you type in the mutation keyword like this, and then you hit the brackets there like that, hit Enter.
[00:06:25] And then you type in the name of the mutation. In this case, it's newShoe. newShoe takes some input like that, got to do that. And then we need to go ahead and pass that input, which will be input. The input for the new shoe is going to be a brand and a size, they're both required.
[00:06:43] So I'm gonna do that, get out of here. Brand, Jordan, and size, nine, cool. And then I can just, I'm returning the input directly if you saw my resolver. So I can just ask for the same stuff and I'll get it back. I'm not saving the stuff anywhere.
[00:07:03] And, oops, I've put nine as a string, it should be an int. There we go. And boom, I get back my Jordans. So that's a mutation. You noticde the only difference thing here with doing a mutation than a query is that we have to put this keyword mutation in front of our query object here, whereas if I didn't do that, it would think this is a query.
[00:07:23] So you can see right there, cannot query field newShoe on type query, because new shoe doesn't exist on type query. It exists on type mutation. So you gotta put the mutation keyword in front of it. That's the difference between a mutation and a query for now. I think this is one point to talk about, I've had people asked me this before is, well, if mutations are creating things, what do they return?
[00:07:45] Well, I think that's up to you and your application and your use case. So what I've got in the habit of doing is always returning the thing that I mutated. So if I created something, I return that. If I updated something, I return that after I updated it.
[00:08:03] If I deleted something, I at least return the ID. And the reason I do that is because if you're using a client, you're gonna learn tomorrow, client side GraphQL implementations like Apollo, they cache stuff on the client side, like you would do in Redux and things like that.
[00:08:17] They need to know about mutations in order to update the client side cache. So if we delete something, they need to know about what got deleted. So they might need an ID to delete it from the cache. If you created something, if you don't send that back, they're just gonna go ask for it anyway.
[00:08:30] So you might as well just send it back so they don't have to ask for it again. If you updated something, same thing. You might as well send back the updated state of that, otherwise they're gonna have to do another query and go get it again. So it really depends on your use case.
[00:08:43] But to me, always just get in the habit of returning the thing that you mutated. Otherwise, the client that's interested in this data is gonna have to make an additional query to get the mutation that you just applied. So you might as well just send it back. There's no standard.
[00:08:59] There's nothing stopping you from doing whatever you want. But that's just something that I picked up. And that's what we're gonna do for the remainder of the course.