Using Composition API0:00
So far, I've shown you a few ways you can do queries and mutations, and both ways should be compatible with both Vue 2 and Vue 3. However, if you're using Vue 3 and Apollo version 4, then you also have the option to make use of the Composition API and Composition functions. So I'm going to use a separate project to demonstrate this, and then we'll continue working with the project we already have. So I already have some boilerplate here, we'll take a look at queries, queries with variables and mutations using the Composition API. But first, let's install Vue Apollo V4. So we already did this earlier, but there's an additional step for the Composition API.
Installing Vue Apollo v40:29
But first, let's install Vue Apollo V4. So we already did this earlier, but there's an additional step for the Composition API. So let's go ahead and install this. Okay, let's go ahead and do all of this stuff here, create an Apollo client instance. So we can grab all of this. We'll put it in our main.js. Let's just put it here, okay. And let's grab all of this stuff here. Let's just put it right here, okay. Let's make sure to update the URI here.
Connecting Apollo Client1:26
Okay, now I'm going to run my server again with npm run serve. I am using a Vue CLI up here, okay. And now we have to connect Apollo to the client. So let's follow these steps here. We're using Vue 3, so we can grab all of this for the imports. So let's paste that up here. And we already have Vue here. So let's add provide and h up here, okay. Let's get rid of this, okay, next. This will now be our createApp method call right here.
Let's get rid of this, okay, next. This will now be our create app method call right here. So let's do this instead of this here. So let me just comment that out, space this. And from here, we can use the store, the router, and we can mount it as well. So mount to app, okay. Save that, and hopefully I installed it correctly. Let's double check our browser to make sure there are no errors in the console. Okay, so there are no errors. Let's go ahead and take a look at how we can do queries or simple queries.
Running Simple Queries2:30
Okay, so there are no errors. Let's go ahead and take a look at how we can do queries or simple queries. So we can go to Guide or right here, Queries, okay. Scroll down. So you can see we have access to this useQuery composition function or hook, however you want to call it. And it's pretty similar. We just use that function and pass in the GraphQL query. And then the result should be in this variable here. So let's go ahead and try that.
And then the result should be in this variable here. So let's go ahead and try that. So I am going to grab all of this and we'll replace the query with our own. So I'm just gonna do everything in home view. This is our boilerplate, mostly just set up for the mutations, which we'll do in a second. So we have some state for the title and the body and also an error. So right here, let's go ahead and paste that in. We just want to grab the posts. So let's go to GraphQL playground.
We just want to grab the posts. So let's go to GraphQL playground. Here is a simple query to grab the posts. Okay, so let's grab this, okay, back here. Let's update this query and it's good practice to name your queries here. So we'll say getPosts and save that. And now the result should be in this variable here. So for now, let's just console.log this result and see if it's correct. So here, we can make use of w,atchEffect in Vue, okay? Hopefully that imported watchEffect, there it is.
So here, we can make use of watchEffect in view, okay? Hopefully that imported watchEffect, there it is. And we can just pass in a callback and console.log result. It's actually a ref, so we have to do .value whenever we're using it in the script, okay? Save that and let's see if we get the results from the GraphQL backend. So back to our app, looks like we have an error here. You can see we have useQuery is not defined, so I didn't import that. So let's make sure to import this right here from vue-apollo composable, okay? That should have imported right here, looks like it didn't.
So let's make sure to import this right here from view Apollo composable, okay? That should have imported right here, looks like it didn't. Actually, it's right there, sorry. Okay, so this should work now, hopefully. Let's refresh. GQL is not defined, I forgot to import that as well. Okay, there's the import, let's try again. And now there are our posts, cool. So we have result, posts, data is where our actual posts are. So now as you expect, we can make use of this in the template.
So we have result, posts, data is where our actual posts are. So now as you expect, we can make use of this in the template. So let's make sure to expose that result first in the return statement, okay? And let's go ahead and add something here for our queries. Let's delete this. Let's just make a new container here, and let's say ul, and let's just check if there is a result first. So we'll do v-if equals result, and if there is, we can loop through them. So let's make a list item, v-for equals post in result.posts.data. And the key is going to be post.id, okay?
So let's make a list item, $v4 equals $post in $result->posts->data. And the key is going to be $post->id, okay? And we can just output the title here, so $post->title. Okay, did I do that right? And there are our posts, cool. Now, what about loading spinners and errors? It's pretty straightforward. You can take a look at that in the documentation. We can destructure it from the useQuery hook here, and same for errors. So let's do that.
We can destructure it from the useQuery hook here, and same for errors. So let's do that. So let's go back to our hook or our compositionFunction down here. Let's say loading and error. And if we're using that in the template, and we are, we have to expose those. So let's say loading and error, okay? Now we can just use that in the template. So here, I'm going to make a new div for loading. We'll say v-if loading, then we'll just do loading, dot, dot, dot. We'll do another one for v-else-if.
Queries With Variables7:08
Hopefully, we can see the loading indicator when I refresh. And I do see it here quickly when I refresh, okay? Let's check out the error. So I'm just going to change the endpoint URL. So we get an error. So let's just change this. And let's see if we get an error, and we do, cool. Let's take a look at a query with variables. So let me just duplicate what we have here. And I'm not going to do the loading and error state, just to keep it simple.
So let me just duplicate what we have here. And I'm not going to do the loading and error state, just to keep it simple. But make sure to rename them if you are using them. So for the result, we still have to destructure the result object here. But we are going to rename it like this. So it's a postResult, okay? Let's go ahead and grab a query that has variables. So back to Playground, I have it prepared here already. So just grabbing one of the posts here, given an ID. So let's grab this, let's just make sure this works, okay?
So just grabbing one of the Post here, given an ID. So let's grab this, let's just make sure this works, okay? So let's grab this, let's go back to our query here. Let's replace this, okay, save that. And remember, we have to add variables here when we're using parameters. So let's name it getPost, takes in an ID, that's of type ID required, okay? And here, it takes an ID, okay? And for the second variable of this useQuery hook, we can add the variables. So after this, let's just add an object here for our variables. And in this case, it's just the ID.
So after this, let's just add an object here for our variables. And in this case, it's just the id. And usually, this would come from the URL, we can say route.params.id. But I'm just gonna hard code it here, as it's just a demo. Okay, let's save that. We have to expose the postResult. So let's do that, postResult, and now we can use it in the template. So let's go here, right here, let's replace this. Or let's actually use this and say v-for postResult. And go ahead and output the title, so postResult.post.title.
Or let's actually use this and say postResult. And go ahead and output the title, so postResult.post.title. I believe that's the correct nesting, try that out. And there it is, cool. You can also make use of this useResult hook, if you wanna pick certain objects from the result. So this can help in reducing nesting, if you have a lot of nested objects in your query results. So let's take a look at a quick example here. So this one isn't too nested, but let's just do it anyways.
So let's take a look at a quick example here. So this one isn't too nested, but let's just do it anyways. So let's put it down here somewhere, I'll put it after here. So we can say const posts equals useResult. Let's make sure to import that, okay? And the first param is the result we're taking a look at, so in this case, this result up here. Second param is the default value, let's just say null. And the third param is the object you wanna extract. So in this case, we wanna extract the posts object.
And the third param is the object you wanna extract. So in this case, we wanna extract the posts object. So we can give it a callback and just say data.posts. So now let's go ahead and expose this posts variable down here, or let's put it after the result here, posts. And now we have less nesting. So in this case, our results aren't too nested, but let me show you what it does. So here in our first example, instead of looping over result.posts.data, we should now have access to the posts directly, and this should still work. Back here, and it still works, cool.
Creating Mutations10:36
we should now have access to the posts directly, and this should still work. Back here, and it still works, cool. Okay, now let's take a look at mutations, and we'll be making use of this useMutation hook. And as you see here, it says it's a good idea to rename the mutate function like they're doing here. And then you can make use of this in your script or in your template. So you can execute the mutation. So like I said, I already have some boilerplate set up for our form here. Let's take a look at that.
So like I said, I already have some boilerplate set up for our form here. Let's take a look at that. So we have this form, we have state for our title and our body. And we also have this createPost method. So let's create this method first, anywhere in our setup method. So let's put it underneath all of this. Let me comment this swatch effect out, and I'll just put it here. So function createPost, okay? And let's just console.log something in here for now to make sure it is being called, creatingPost.
And let's just console.log something in here for now to make sure it is being called, creatingPost. Okay, let's try this out. Actually, we have to expose this, createPostMethod, and this should work. Hopefully, looks like I accidentally imported this, let's try again. Okay, and if we open up DevTools, hit this createPost, we do get that console.log. Okay, so now let's go ahead and grab the useMutationHook or the example here. So it's all of this. Let's put it right above our method.
So it's all of this. Let's put it right above our createPost method. So createPost method, okay, let's put it right here. Let's make sure to import the useMutationHook. So from within view composable. So let's rename this to createPost, okay, same with this. And we have our mutation in our GraphQL playground. So right here, let's uncomment this. Actually, I'm gonna use this, so we have to rename it again. So I'm just hard coding the userId here, and we have a title and a body.
Actually, I'm gonna use this, so we have to rename it again. So I'm just hard coding the userId here, and we have a title and a body. So let's go ahead and paste that in. So I'm going to replace this, let's paste that in, okay, save. And again, we have to name our mutation here. So say mutation createPost, we have to accept the params. So it's userId is ID, we have a title, that's a string, okay, and we have a body, that's a string as well. Okay, and then here we can make use of those variables. userId, title is title, and body is body.
Okay, and then here we can make use of those variables. User ID, title is title, and body is body. Okay, let me save that. And for the variables, we can pass those in when we're making use of this createPost mutation within our code. So you can either use it in the template directly, and if you're doing it that way, make sure to expose it in the return object. Or you can use it in your script directly, and that's what we'll do here. So within our createPost method, let's get rid of this. Let's use createPost here, and we can pass in the params.
So within our createPost method, let's get rid of this. Let's use createPost here, and we can pass in the params. So userId, we're just going to hard code that to one. title is, remember we have this ref up here that we can use, and that's being bound using v-model. So we can say title, and it's a ref, so we have to say title.value. And same for body. So body is body.value. Okay, and after that's done, let's just reset the form. So we can say title.value equals an empty string, and same with the body.
Okay, and after that's done, let's just reset the form. So we can say title.value equals an empty string, and same with body. Okay, does this work? Hopefully it does. Back to our code, or sorry, our browser, I mean. Let's refresh this just in case. Testing, mutations, testing, createPost, and I think that worked. The form did reset. This error is just my last pass, I believe. So let's check the database to make sure.
This error is just my last pass, I believe. So let's check the database to make sure. And there it is. Cool, so it did work. Now, what about loading states and error messages? Let's take a look at that as well. So for loading, we can do the same thing and destructure loading. And let's make sure to rename that as well. Let's say createPostLoading, and since we're using this in the template, we have to expose it.
Let's say createPostLoading, and since we're using this in the template, we have to expose it. So createPostLoading, and let's just use it in the template. Let's just put it next to the createPost button, wherever that is right here. Let's just say div v-if equals createPostLoading. We'll just say loading here. Hopefully, we can see that right as we click Create. Let me just refresh just in case. Let me just resize this. Testing, one, two, three, and I'm gonna look closely to see if I see the loading.
Let me just resize this. Testing, one, two, three, and I'm gonna look closely to see if I see the loading. And I did see it's flashed there really quickly, so it did work. And for errors, you can grab the error as well, or you can make use of this onError event, which I find easier to do. So let's grab this back to our code. So right here, wherever our useMutation hook is, let's grab the error here, or the onError event. So say onError, and let's paste that in here. And here is where we can handle that error.
So say onError, and let's paste that in here. And here is where we can handle that error. So I already have a piece of state to hold the error message up here somewhere. Right here, createPostError. So let's make use of that. Actually, I already did this in the previous video, so I'm just gonna paste it in. So I just wanna grab the first error that Laravel is returning, and then I'm just setting it to this state that we have here. And then we can make use of this in the template, so we have to expose this. Actually, I already am.
And then we can make use of this in the template, so we have to expose this. Actually, I already am. Let's just use it above the form, wherever that is, right here. So let's just say error, or createPostError, okay? And we can also make use of the other event here called onDone. And this is where we can reset the form, so let's do that as well. So onError, there's one called onDone. And down here, instead of resetting the form here, because when there's an error, it's gonna reset the form as well, which I don't want when there's an error.
because when there's an error, it's gonna reset the form as well, which I don't want when there's an error. I just want it to happen when it's successful. So it's pretty much the same as this. Say onDone, this is the result, which we don't really need. And then we can just get rid of this, and then paste in this, okay? Hopefully this works. Let's check it out in the browser. So if I just hit createPost, the validation messages should be returned, and it should output it somewhere here.
So if I just hit createPost, the validation messages should be returned, and it should output it somewhere here. Okay, so that works. Title, this should change to a new message, and the title should still be here, and it does work, cool. And if I do this, this should create it, and it does, cool. We should probably reset the error message as well, so let's do that quickly. createPostError.value equals, was it null or empty string? It doesn't matter, that's fine. So yeah, that's basically it.
It doesn't matter, that's fine. So yeah, that's basically it. If you prefer using Vue 3 and the Composition API, and composition functions or hooks, then go ahead and make use of these features within Vue Apollo 4.
