Check out a free preview of the full Server-Side GraphQL in Next.js course
The "Queries & Mutations" 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 discusses the difference between queries and mutations, with queries being used to retrieve data and mutations being used to modify or create data. He also demonstrates how to pass arguments to queries and mutations, and how to use input types for more complex arguments. Scott also briefly touches on custom directives, walking through an example of how to create and use a directive called "uppercase" to modify the behavior of a field.
Transcript from the "Queries & Mutations" Lesson
[00:00:00]
>> Scott Moss: Okay, now we're gonna talk about operations. We've been using query this whole time. So we'll talk a little more about that, but I'm also gonna talk about mutations which are the opposite of queries. So queries in order to make a GraphQL schema, you at minimum need one query.
[00:00:14]
If I got rid of this, this is gonna break. I got rid of all these queries here, I'm pretty sure this my thing will not load up.
>> Scott Moss: I mean it will break, it won't work. You have to have at least one query. You don't need a mutation, we don't have any mutations, everything works fine.
[00:00:30]
But you need at least one query I believe.
>> Scott Moss: So queries you can think of those as like get requests. You want to get something from the database. There's something you're trying to get back from this graphical server, those are queries. A mutation is something you're trying to modify or create, you're trying to mutate something on the server, so those are mutations.
[00:00:49]
You could think of those as post, put, delete, queries are get, get one, mutations are post, put, delete as far as crud goes. So the other thing we can do with queries is that we can take arguments, right? So if I say, cool, you wanna search, but what do you wanna search for?
[00:01:07]
I can pass in arguments here, and these arguments could have different types. So I can say, I wanna search for search results. Let's see we got tweets and profiles. So tweet has an ID profiles have ID. So I'll say I wanna search for an ID, whose type is ID.
[00:01:27]
>> Scott Moss: So this is how you would pass an argument to a query. And then you'd have access to this argument and the code that you would write to resolve this and this is also strictly typed. You can pass as many arguments as you want, it doesn't really matter, but eventually they get so long, it's just like code.
[00:01:46]
If I make a function that's more than two arguments, I'm just gonna get rid of those arguments and make an object. Now you gotta pass in an object, more than two arguments is gross. So I'm just gonna have an object that you gotta pass in with the properties on it.
[00:01:56]
The same thing is true here, you will make an input type. So if I were going to make something more complicated, I could say, input SearchInput, using the input keyword here. And then name spacing your input this is not required, but it just makes more sense for me as a human and when I go read it in the documentation that I know that the input type.
[00:02:18]
And then it's kinda just like an object type, you can search for an ID, it's optional. Maybe you wanna search for a name, that's optional. So I'm not gonna make it required, now you can go down here, and it's probably more appropriately to call this input, I usually always just call it input.
[00:02:35]
You can call it whatever you want, but the type is SearchInput, and it's required, you have to pass in an input here in order for this to work, right? So I can save that. Let's make sure we didn't break anything, let's go back here, still works, let me get rid of this.
[00:02:52]
I'm gonna do a query for search. And notice down here this variables panel popped open. So you can see I have Arguments now. I have one argument whose type is input, so it's an object whose type is input. I'm gonna click into that and I'm gonna say yeah, ID and a name, so I'll have to fill those out.
[00:03:10]
Well, I don't have to because they're both optional, but I will. And then, same thing, just give me this stuff. So I'll say, I want that, give me the name here, and then I'll pass up for an ID. I don't know how this works with mock data, so I'll just do that.
[00:03:24]
And for a name, I'll put Jess, and we'll see what happens. So you can see, yeah, it's still just gonna return mock data, it just kinda ignores this. But this is how you would pass in the variables. Notice on the query though, you have this weird syntax, I talk a lot about this syntax in the client side course.
[00:03:42]
But the best way I can describe this is you can think of this right here as an operation or you could think of it as you creating a function in JavaScript. This is you making a function and this is you describing the arguments for that function, right? If you were doing this in Typescript, you say, here's the argument name, here's the argument type, it's the same thing.
[00:04:02]
It's just that all arguments in TypeScript have to start with a dollar sign. That's how it knows it's an argument. That's the only difference between this and making a function in TypeScript. I'm making a function called search. So you can call this whatever you want cuz it's a function, it doesn't matter.
[00:04:16]
And I'm declaring an argument called input, whose type is SearchInput and it is required. And then on the next line, I'm calling a function called search. This search function already exists, I'm calling it, it is demanding that I pass in an argument called input whose value is this variable.
[00:04:39]
So this is me making a function and declaring an argument, this is me calling a function and passing in the argument. It's the equivalent of if you were in JavaScript and you were making a function called action, and action took in some options, whose type is a Record of,
>> Scott Moss: I don't know how this works, this is like a string, I forgot how Record works.
[00:05:16]
I don't know, you'll have some type here, let's do this name string, okay, name string. And then we have that, and then somewhere in here, I'm gonna call a function that already exists somewhere, right? It could be like thisFunctionAlreadyExists.
>> Scott Moss: And I'll call it and I'm gonna pass in options.
[00:05:42]
That's the equivalent of that, it's the same thing, it's the exact same thing. I'm making a function, I'm declaring arguments, now I'm passing it into a function that already exists. It's the best way I can describe it.
>> Scott Moss: So that's what we're doing here and then yeah, you run it, you get some stuff.
[00:05:57]
I promise we're gonna get to making the app, there's a lot to cover here. So mutations, let's do some mutations, I'm curious how this works with the mock schema, I haven't done this in a while. But mutations are how you make things, so they're very similar to queries, in fact, they just make stuff, so let's make some mutations.
[00:06:14]
So the way you make a mutation is you say, type
>> Scott Moss: Mutation. I can spell, there we go. And then let's say I wanna make a tweet. So I can say makeTweet, okay? You gotta make a tweet, you gotta pass up some stuff. Let's say you gotta pass in some content.
[00:06:35]
So give me some content. Let's type String, required, and I'll. And I'll return back a Tweet that's always there. And it's not nake, it's makeTweet. So now I have that mutation, I can go back to our app or Apollo Studio. If I go back here, you can see now I have a mutation on the root type.
[00:07:02]
So I'm just gonna uncheck that, get rid of this. I'm gonna click on the mutation, I only have one called makeTweet, so I'm gonna add that. I want the content, I want the ID. I have to satisfy the request of the non-null content string, so I'm gonna go down here and say, this is my tweet.
[00:07:23]
>> Scott Moss: And I'm gonna run this. And now I made a tweet.
>> Scott Moss: That's it, it's very similar to queries. In reality, it's the exact same syntax as running a query, it's just the behavior on the back end is different. You're creating something, you're modifying something, you're deleting something, whereas a query is expected to just get something.
[00:07:44]
Now, does GraphQL actually care or know what you're doing on your resolvers? No, it has no idea. Just like if you were to make a REST route that we're supposed to get all your to-dos, there's not a stop of you from creating something there as well, it doesn't care, right?
[00:08:00]
So, there's no checking or knowing what you're actually doing, it's just that's the convention you should be following. Mutations are for modifying and deleting and creating, queries are for getting. But obviously you'll have like, side effects. One side effect might be, I'm doing a query that gets data, but I'm also making a post request to my analytics service that logs this.
[00:08:22]
So you still might be modifying something inside of a query, you might have side effects and things like that. So it's not always one for one, but that's the expectation, queries and mutations. Any questions on mutations? They're literally the same thing as queries, your logic is different on the resolving side.
[00:08:38]
And yeah, you can use inputs the same way you did them with queries.
>> Scott Moss: Subscriptions, we won't be diving into subscriptions today, but subscriptions are basically web hooks, I'm sorry, web sockets. If you want real-time communication with the GraphQL API, you would use subscriptions so you can subscribe to something specific.
[00:08:59]
I don't know if the mock schema, will allow us to do that, I'm not sure. But this is for WebSockets, we also couldn't really do this on Next.js anyway cuz Next.Js has service functions and you can't do WebSockets in a service environment. The way the WebSockets work they need a long-lived connection a service function, spin up and shut down to do transactional things.
[00:09:24]
So you couldn't use WebSockets in a serverless environment without a third party service sitting in between them, and that's just a lot of work right now. But that's how you would do WebSockets, two-way communication. We talked about input types, we talked about arguments. Okay, that's all the schema definition language that you,- this is literally 99% of GraphQL schema if you know this, you know how to make a schema.
[00:09:45]
There's really not much more other than custom directives, advanced union types and things like that. You can even make custom scholars I guess I didn't really talk about that but we have the scholarships. You can make custom ones if you want a date field maybe, or something like that.
[00:10:01]
You can do that as well, but that's it. If this is all you ever knew, you can make any graphical schema, so we're past that part. How does GraphQL know whether to run before or after the directive? It's up to the directive to decide when it wants to run.
[00:10:19]
I think there's gonna be a lot of directive questions, so I'm just gonna show the documentation on what that would look like. Because it is kinda what the hell's going on there? How's that working? I'll just show you what a directive might look like as far as making one.
[00:10:34]
Let's see if they have an example of custom directives implemented here. Okay, this is not what I'm looking for I'll show you custom directives graphql.
>> Scott Moss: So let's say you wanted an uppercase directive, let me make this bigger. You wanted to create a directive called uppercase. So first thing you do is in your schema, you would use the directive keyword.
[00:11:01]
You would say this is the name of the directory that I want you to use the on keyword and this is a enom of where this directive should work. So in this case, the field definition so that means you can use this uppercase directive on fields. There are other different definitions on which a directive can work on.
[00:11:18]
And this example they want to be able to uppercase fields, right? So, then in their code you can also pass in argument stuff, but then in there, let me see here all the value all the supported location. So scholar object field definition, argument definition, interface unions, enoms you can let him make a director for anything, okay?
[00:11:39]
And then let me see if they have the code that I wanna show you. So here's the uppercase one, this one's a little more, yeah, there we go. So on the object field, so executes once for each object field in the schema. So every single field that you have in the schema, this function is going to run, right?
[00:11:56]
And then what it attempts to do is, hey, does this field have the specified directives that I'm trying to check for? If it does have the uppercase directive, what I wanna do is I wanna get the config for that field, which in this case returns the resolve function.
[00:12:15]
The resolve function, we're gonna create resolvers in a minute, but this is the function that runs that returns the value of that field. So what you can see here is they are overriding that resolve function. So they're basically creating a wrapper, it's like a higher order function around the original resolve function for that field.
[00:12:34]
So let's say the resolve function for that field was literally just a function that returns the name. What they're gonna say is, okay that's not what it is anymore, what it is now it's gonna be this function, and then internally, inside of the function that they create, they call the original resolve function.
[00:12:50]
So this is how they get the value of it. Once they get the value of it, then they go, is it a type string, cool, uppercase it and return that. This is a very common pattern, it's not something unique to GraphQL. I mean, you do this in React, it's basically middleware.
[00:13:04]
You're applying middleware to, in this case, a field. So you're like, hey, I'm gonna override the original functionality of what normally happens here, and I'm going to say it is this now. And inside my implementation, I'm gonna call the original one to get the result unmodified by whatever code I'm doing.
[00:13:21]
And then once I get the result, I'm then gonna modify it and return that modified result. So that's what GraphQL is gonna see. So in this case, it's quite simple, it's basically what I suggested earlier. They wait until the data is resolved and then they run a function on it, and that's what's happening here.
[00:13:36]
It's not too crazy, but it's quite powerful. And this was what I'm showing you is gonna be specific to the GraphQL server framework that you're using. They all do it differently, they all have different versions. That's why I was like, this doesn't look too familiar cuz I use something else that uses a different version and it's slightly different, but the logic is basically the same.
[00:13:59]
I'm hijacking a resolve function, I'm recreating it by calling the original one and doing some some type of thing. So it can get pretty crazy.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops