Build Apps With Vue 3 + Apollo Client 3
with Natalia Tepluhina
Combining the powerful new features in Vue_js 3 and Apollo Client 3 means we can do some really exciting stuff with our apps! In this episode, Natalia Tepluhina teaches how to do it!
Resources & Links
Captions provided by White Coat Captioning (https://whitecoatcaptioning.com/). Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.
Jason: Hello, everyone, and welcome to another episode of Learn with Jason. Today, on the show, we have, I'm going to get this right, Natalia Tepluhina. Did I get it right?
Natalia: Yes, almost right.
Jason: As close as I am ever going to get. I'm super excited to have you on the show. Thanks for being on here, because we are going to talk about some really, really fun stuff, which we'll get to in a second, because first, I want to talk about you. For those of us that are not familiar with you and your work, do you want to give us a little bit of a background?
Natalia: Of course. My name is Natalia, and my last name is Tepluhina, so Jason was almost correct on this. I am a VGS core team member. I care mostly about documentation, so if you are reading Vue 3 docs, this is a part of my work, together with the Vue docs team. I have a full-time job, working for GitLab, a staff engineer working for GitLab by day, Vue by now, normal open source development.
Jason: Like a true superhero, developer by day, developer by night.
Natalia: Open source by day, open source by night.
Jason: ( Laughter ) definitely. Well, I'm really excited, because we are going to talk about something today that I feel like I know reasonably well, which is graph QL, but then we'll talk about something that I don't know about at all, which is Vue. I did an episode where we looked at Vue -- several episodes, but I haven't really learned Vue. I have managed to put things together to make Vue apps work but not to actually understand, right? So I'm really excited to dig into this. But we are specifically talking about Vue 3 and Apollo client 3, both of which are pretty fresh, right? So, in your opinion, what's the most exciting thing about Vue 3? Why are you excited about it?
Natalia: A few things. First of all, as a person who works on enterprise application by day, I would say performance, because Vue 3 gives us a free performance boost without refactoring our components to functional ones. It's like a great thing, because we have performance issues, and this is going to solve them.
The second is not for my everyday work but for a person, for me myself, because I have an extensive experience with Angular. I like -- I have Stockholm syndrome. I'm sorry. So, Vue 3 is written with type script, and it gives you type script support right from the box, and it's really great, because everything is typed without explicitly writing typings, which is amazing.
And third part that we'll be touching on slightly today is a composition API. For React people, it is something similar to hooks. It is not hooks but similar to them. This is a way to abstract logic nicely and to reuse it nicely as well in a functional style. So, for Vue, it's kind of a new thing, because previously you could abstract logic on the exponent level. You could create mixings. This is a moment of pain for everyone who used mixings, and you could work with renderless components, but right now, we also have a way to abstract logic that I'm very happy about.
Jason: Very, very cool. And when we are talking about GraphQL, I found what I like about GraphQL is I'm really fond of the discoverability of it. I like if I build a GraphQL API, I can send it to my coworkers, say here's the API, and I don't have to write a bunch of documentation or send them detailed specs of what it's going to look like. They can just kind of look at the playground and figure out how things are going to work. That to me is always one of the biggest benefits.
I'm curious if there's anything that you find specifically exciting about it, you know, that makes you want to work with this pattern.
Natalia: First of all, as you told, is discoverability. Second one is basically what you ask for is what you get.
Jason: Uh-huh, uh-huh.
Natalia: And this is very important if you work with huge structures of data, let's say, a repository, and like a small example, not from GitLab but from GitHub, GitHub version 3 API which is ( off mic ) API. Eventually, I want to search a list of repositories from a certain organization, and I want to know what languages were used in every repository and how many forks does the repository have. First step, a list of repositories, API call. Every repository is a huge list with 200 -- on every object. Do you think I get languages there? No, I have a link to a languages page. Do you think I have forks? No, I have a link, of course.
So, now I'm iterating over repositories. I make two API calls, one for forks, second for languages.
Jason: And that's per repository, so if you query 100 repositories, you're making 201 rest APIs.
Natalia: Yes, exactly. Sometimes you need to make them recursively, because they are paginated. Enjoy.
Jason: ( Laughter ).
Natalia: And then what if I want to use Graph Kill? And I fetch a list of repositories with only the forks number, forks count, and languages, and that's it. One API call, no other fetching, not like all of the data I don't need at all. Easy.
Jason: Yeah, that is super exciting. It's something that I'm really, really into as a general practice, right? Only deal with the things that you want to deal with and not the things that are, you know, like -- we just only want the data that we actually need, right? There's no benefit to pulling in megabytes of data if it's all just going to get trashed when we are done building the page.
Natalia: Imagine if you're on mobile and you have a low connectivity.
Jason: Uh-huh, and when you're talking about static site generators, which is something that I talk about a lot, one of the things that happens is when you make API calls, that data, that API result is going to get embedded in the page. So, if you are using a traditional rest API and you're putting this data into a static site generator, you're actually including the full result of your API calls into the generated JSON for that page with Gatsby or Next or any of these exported sites. So if you are overfetching, that isn't something that gets abstracted away by static site generators. You're going to send all of that data down, so, GraphQL is going to give you the ability to solve these problems.
Yeah, I know there's a lot that we want to do, and I want to make sure that we have got plenty of time to do it, so let's switch over to working on some code.
Before we do that, let's take a second to thank our sponsors. We have got live captioning going today. I still haven't figured out why my site has betrayed me and won't show me the embedded captioning, but it's at StreamText.net, which I have just pasted a link in the chat. You can see live captioning happening right now. This is made possible through the generous contributions of Sanity, Auth0, Netlify, and Fauna, who all pitch in to make this more accessible to more people.
I just realized I have double chat boxes going on. I fixed my overlay, but it's causing other issues. Slowly but surely, this stream is going to get dependable again, I swear. In addition to live captioning, make sure to check out Natalia's Twitter. Follow her. My site has become self-aware, yes, that is correct ( laughter ). But go follow Natalia. It is a nonstop stream of great insights and a lot of things, and also, if you are feeling generous today, I am currently matching up to $2,000 in donations to Black Girls Code. So if you are looking for a tax deductible donation to make, go make it today. Send me a tweet with your receipt, and I'll match it, up to $2,000 US.
Okay. So we have Vue, and I went to V3.Vue.js.org, and I'm also looking at the Apollo client. Here, is this the right page?
Natalia: Yes, I think this is the right page, and we'll also be using Vue (off mic ) which is an integration. But don't go to their website. Use V4 Vue Apollo, V4.Apollo.VueJS.org. Yeah, this one.
Jason: All right. So these are the tools that we are going to be working with today, and we started a live share, so I have an empty folder in here, and we can see that Natalia is in here. I'm going to remove this one file, and then I think we can just get started, right? So, if I want to -- if I want to get started, what's the first thing that I should do?
Natalia: Let's create a project with Vue CLI, and I hope Jason has Vue CLI installed, because if he --
Jason: I did it! ( Laughter ).
Natalia: All right. So we will create a problem. You type Vue create, and if you have a folder already created, you can just have a dot here, and that's enough I think.
Jason: Okay, here we go. And --
Natalia: Yep, exactly. No, we will manually select features.
Jason: Manually select, okay.
Natalia: Yes. We don't need Linter. I'm just using -- but this is fine. We don't need any CSS pre-processors, but we will be using Vue version. Babel is fine. Leave it where it is. Okay. We are ready. Yes, we are going to use Vue 3. It's not a preview anymore. Dedicated config files -- you are not working with Vue, but fine. I am biased.
Jason: Okay, so this --
Natalia: You have two Vue core team members at Netlify.
Jason: We do, with Sara Jazner, and we just hired Ben Hong, which we are excited about. That is definitely a strong argument in favor of Vue, but they are going to have to fight me and Cassidy over it, so ( laughter ) --
Natalia: This sounds like an interesting Netlify challenge. I would say make a fight and whoever wins, that's the framework.
Jason: ( Laughter ) perfect. Did you see when Cassidy and Sara did the chicken fight?
Natalia: No. Oh my gosh.
Jason: So, Cassidy plays this game where you start back-to-back, and then when somebody says go, you have to act like a chicken, and the first person to laugh loses, and it is really funny. There's a video somewhere that I'm not going to be able to find right now, but if you can find it, it's Sara and Cassidy having a chicken fight, and it is one of the funnier things that I have seen. It was at a conference, like maybe JS Conf Hawai'i?
Natalia: Oh, my God, I need this.
Jason: So we have in here a project. I can see that we have Vue, Babel plug in -- and what's this one? Is this compiler components?
Natalia: Probably the visual for the code is not loading all of the files for me, unfortunately. I can only see all of the git.
Jason: It may have -- whatever is git ignored, it may have skipped that.
Jason: Do you need to see the node modules?
Natalia: Not really. So, what we have here right now is basically a Vue project, and would you mind running it? So, you will have a serve screen. So, yarn serve will start NPM.
Jason: It's in either. This one looks like it was set up in Yarn, so we can use whichever, and here is our local start.
Natalia: It's running for me as well. So, beautiful. It's a very default-y application, but I'm glad you find it that way. I like it as well.
Jason: What I do like about this is it actually takes you places. I feel like a lot of projects make the mistake of their hello world is just like a hello world, and this actually is like, hey, you started a project. Here's where to go next.
So, props to you and the rest of the docs team.
Natalia: I think we need to replace a few links here, because they are mostly for the Vue 2 ecosystem. But still -- and by the way, have you noticed that the frameworks are color coded, React is blue, and Vue is green.
Jason: I had never noticed that. I guess with our powers combined, that means we can create the whole rainbow.
Natalia: It's true. We have a project. Everything is great. Let's start our definitely great challenge. We are trying to install a client. So, small spoiler, while Vue 3 is released as well as all client 3 is released, both are still catching up with their ecosystems, which means that all of the integrations are way behind. So, what we are going to do right now to make things work is we will be installing Vue Apollo, which works perfectly with Vue 2, but we need to do some really hacky work on it to make it work with Vue 3. So, bear with me.
First of all, we have Vue application. Let's install Apollo client. Let's go to Apollo documentation. I think you have it open. I can do the same if you want, but it's like up to you.
Jason: Okay. So we have got our documentation here, explore docs, it says --
Natalia: It has an installation part. Get started, I guess.
Natalia: And there is like NPM install -- yes.
Jason: And since we have a Yarn lock, I will use Yarn instead. I'm going to try to remember to use Yarn. I have been like -- I feel like every month, I change which one I use. I'm like, oh, yeah, Yarn is better because of mono repos, and then it's like, oh, those make my life hard, so I'll use NPM, and back and forth and back and forth.
Natalia: We have the Apollo client installed. Now my internet connection is unstable. Probably Visual Studio code is eating all of the connection bandwidth possible.
So let's start with creating our client. For now, let's forget about any Vue integration. We just want to create a client and make a first query with very simple promise, no smart queries ( off mic ) -- at home.
So, let's import two things from Apollo client, which is actually Apollo client and creating in memory cache, and let's start working on the client. Don't you mind me typing, because it depends like if you are fine with this.
Jason: No, no, go ahead, this is great.
Natalia: So, let's create default client, and this will be a new Apollo client instance, and here, we need to specify two things in our application. First is the URL of the server, and please let me know if you have that server running locally.
Jason: Let me check. I think I started it. Let me make sure.
Natalia: It should be on local host 3000, if it's running.
Jason: I really don't like this -- is this on Vue 2 by chance? Do I need to install the old?
Natalia: No, it's just the Apollo client, so it doesn't care about any kind of framework. It's just Apollo server. It's fine. We can use the -- API.
Jason: Okay. That's so weird, because I have -- I have it.
Natalia: Okay, no worries. I will use ( off mic ) API for now, and if you --
Jason: Oh, weird. I'm getting a Yarn --
Natalia: I know.
Natalia: What is it saying? For me it's very blurry, so I'm trying to recognize it.
Jason: It's saying that the yarn package flat map stream is 404'ing, like the package doesn't exist or something. I wonder if it will find it in NPM.
Natalia: Meanwhile, I'll create a link, so we need to specify the URL. If you are about to compare APIs, this is a really great thing.
Jason: Yeah, it's wonderful.
Natalia: And let's create also a cache, which will be new in memory cache, if I'm correct. It should be fine. I'm checking the docs in the background, yeah, it's correct.
Jason: I'm the same way.
Natalia: So, let me share a link to you using the API, so we'll be able to check the playground and show how it works.
Jason: I copied the code. So here's -- all right. So, in here, we can look -- and this is what we were talking about when we were talking about the API being discoverable, right, is that if we come in here, there's a docs over here where we can see everything, or my favorite shortcut, if you hit control space, it will just pop up all of the available things, so if we want to get a list of characters, then we can look in here, and let's see, we'll get some results, and then let's just get the name. And we get -- it's beautiful. And look, we only got the name. But if we want something else, we can get that too.
And this is what Natalia was talking about when she said that you only get the data you need. You're not overfetching. You're not loading a bunch of stuff that's just going to remain unused, and, you know, as you get into APIs that have tons and tons and tons of data, you can imagine like how much of an impact this will make on the size of the data that you're sending down.
Natalia: Exactly. So, let's try to use this query in our application. Let's just copy/paste whatever you created for characters for the GraphQL playground. I mean, it's not GraphQL. It's Prisma playground, right? Let's call it playground, because it is. So, let's create the first query here. Yep. And here, we will need to use a GQL, right, because we need to use a templater, and that's going to come --
Jason: I think it will come out of the client, right?
Natalia: I'll import it here. So it will be import gpl from GraphQL-tag. I am not sure. I'm quickly checking the documentation here as well. Oh, by the way, you were correct. They have it in client 3.
Jason: Oh, nice, very cool.
Natalia: It's embedded now. I got used to Apollo client 2.
Jason: Yeah, but I feel like that was one of the major pain points of Apollo client 2. Everything was to modular, you needed 15 packets to use the client, and this was nice that they took that feedback and brought it all back together for V3.
Natalia: Exactly, because you were installing I think like ten different packages. Previously, the link was state, it was Apollo link state.
So now we have an Apollo client and we have a query, so let's make first call. We will be using default client query, and here, we need to specify the query action. Right?
Natalia: And then we need to resolve our -- so let's console log it. We have the response, and let's do my favorite step. I always have this joke at conferences, because if you're a Google developer expert, you get to really know Chrome developer tools, and for me, it's like using console all the time and debugging with console log and enjoying console log with everything, explaining console logs. I'm a console log developer advocate.
Jason: I feel very much the same. People continually show me how to use the debugger, and I'm always blown away by how powerful it is, and then I never use it.
Natalia: Let's go back to console, because I can console log everything.
Jason: Okay. So I'm starting this up, and that should give us a --
Natalia: A list of characters, yeah.
Jason: Oh, wait, something went wrong. A dependency was not found. We are missing --
Natalia: I guess what dependency is it.
Natalia: React. So now is the best moment. That's why I'm on this stream. We have got to install React into Vue application.
Jason: ( Laughter ) okay. So I'm going to Yarn add React.
Natalia: And don't ask why, because when I discovered this, my main question was like, Apollo, why? Why are you so cruel to me? Why you make me install React to your app? But probably they will change it to be peer dependency at some point, I would guess so. But it's very funny, you get to install React. But we have an application running.
Jason: We have an application running, and --
Natalia: Let's check the console.
Jason: So we need to parse it as JSON, but we got a response. Let's see. So we can just take this here, and we will add one extra log, or we can do res JSON, and now we should see our actual characters here, I think, maybe.
Natalia: Maybe ( laughter ).
Jason: Ooh, it's not. It's not a response. What did I get wrong?
Natalia: Let me check the code. Hm... it looks correct.
Jason: Response, body, it's a readable stream... huh.
Natalia: The main thing here, we got a response. We are not going to be using it.
Jason: Okay, fair enough.
Natalia: It's fine. We were correct. We were hitting the API. We are getting to 100. We are happy.
Jason: In a related note, I was able to get the local API running.
Jason: So now we have a list of wines. So, let's take a look at this.
Natalia: And this list of wines was really ugly, because I think I was adding a list of test wines, so I'm sorry. I screwed up a list.
Jason: This is great. This is a great list, because we have got a bunch of wines here, and if I add -- let's add a description and maybe a variety. Beautiful.
Natalia: Yay, lots of stuff.
Jason: Yeah, okay, perfect. And the reason that we wanted to use this one is that this adds something new, which is mutations.
Natalia: Mutations and subscriptions. We'll see how it goes.
Jason: Oh, wow. Subscriptions. Let's get serious in here. Okay. Now if we want to, we can pull in data from our own API. So, if I dropped this one in instead, and we do this, let's see if this one works for us. And I guess I can prefix with query, just for the sake of clarity, and then if I come out here, it doesn't like our response in the body for some reason. Does it need to be like that?
Natalia: No if you were to --
Jason: Got it. I don't know why it doesn't like our --
Natalia: Try with JSON and we'll see how it goes.
Jason: Oh, here it is. So, GraphQL is doing the JSON parsing for us. So here's our list of wines. Perfect.
Natalia: We have all wines. We have it fetched. Now let's try to make Vue and Apollo client friends, which is not that easy. So, we will be working with Vue Apollo as an integration, and for this, we will go to Vue Apollo installation part. We will skip all of the Vue CLI 3 project. We will skip everything and so on.
Jason: Oh, installation part, got it.
Natalia: Yep, but don't try to add Vue Apollo. This is for the CLI -- it will try to install and fail, trust me. So the only thing that we need to install right now is Yarn add Vue Apollo, but we will be using a very particular Vue Apollo.
Natalia: So if we scroll down a bit, I'm just trying to find the correct place there, because if you're going to use this documentation, it's a bit outdated. It's exactly for Vue 2.
Natalia: So all of the Apollo boost, you can say it's an Apollo client as well. So, we have Apollo client installed, and you can click on next steps, and you will see that there are three different APIs for Vue Apollo, like, Option (Classic), and -- ha-ha-ha, classic. We'll go with composition API. You can click on the composition API, and you will see NPM install. So we are going to install this, and we are going to use Yarn.
Jason: Okay. Yarn add Vue Apollo composable. So now --
Natalia: There will be some magic doing this, and let's start -- we will have errors. No worries. This is expected.
So, what we are going to do here, again, this is the code you would need from Vue 2. For Vue 3, we will still need to import the default Apollo client from Apollo composable, so let me do this for us.
Natalia: And you can see that the text here is slightly different from what you can see on the documentation. There is no more new Vue, and render looks different as well, and you may ask like what is going on here. So Vue 3, we changed the way the bootstrap views application. Right now, we are not creating a Vue instance. We are creating Vue app, and we are mounting it. That's why I will need to change the syntax respectively for our application as well. So, bear with me.
First of all, we will be using set up provide. It's kind of the same. So, this part is not changed that much. The only difference here, let me just quickly go back to the code. So, the only difference here -- oh, wow. My -- wait a second. My live share -- yeah, it's like my live share for a moment gave me a total freeze. So, we will be doing the same with the setup. So, here on the create app, we are going to have a setup function, which is instead of app, we will be using an object. So, here, we had to find a root component instance. Previously, it was app, because we imported it from here. So, right now, we are going to use the same, like, it's not formatting for me. So, instead of Apollo client, we will be using default client.
Jason: And that's the one that we set up here.
Natalia: Exactly, we just created it here.
Jason: Got it.
Natalia: But here, it is provide, and in documentation, it is imported from the composition API. Let me explain this cryptic magic.
Natalia: So, at Vue composition API is a plug-in for Vue 2 to use this API with the old version, so it's kind of like a bridge.
Natalia: Here, we have Vue 3, so we don't really need to import it from composition API anymore, but we will need to import provide from Vue, and if you wonder what provide is, what is going on here, if you worked previously with React, provide and inject is kind of similar in the React context. If you provide something in the root instance of the application, this default Apollo client will be available in every single component. You don't need to test as a prop with all of the component chains. You can simply refer to it whenever you need.
Natalia: And it's also great for dependency injection, so what we are actually doing here is dependency injection.
Jason: Makes sense. Okay. I'm following so far. How you feel, chat? You following?
Natalia: It might be a bit complicated.
Jason: Yeah. I think people are with us here. So, this is exciting, right?
So, and I like that it's being, you know, again talking about consolidation, like taking feedback and consolidating these APIs so that it's all approachable in one place. This is really nice.
So, now that we have got a provider, we are able to provide the default Apollo client and the default client, and basically what we are seeing is we want both of these things to be available in any child component; is that correct?
Natalia: Yes, exactly. It will be available everywhere, and under the hood, our composition API will be using this as a default client. Previously, you would need to add Apollo client to Vue prototype using Vue use Vue Apollo, but I think for me, it always feels like if you're adding something to the prototype, it's a very hacky way, and it doesn't feel clean to me if you refer to something in the prototype chain. It's like, okay, now it's a bit better. So we did provide, but we need to render something. We actually need to render our app component.
Natalia: So we will add also a render function here, and the render function would be -- it's just render, and you can see the benefits of type script. Even though we are not using type script here, it's already like giving us all of the signatures, everything what we need here.
Jason: I think, yeah, it's showing on your screen but not mine. So let me see what happens when I go to do this. There we go. So, I think it might be because I have everything in my VS code turned off to be less noisy during streams.
Natalia: Ah, okay, okay.
Jason: So, we ran into this last week with Ben where to get the type script stuff, I had to use my other instance of VS code that doesn't have everything turned off for streaming. So if you want to see how that works in VS code, check out that episode. I'll drop a link to it real quick. But for now, you'll just have to trust us that it works, because it will take way too long for me to go and set up a new live share.
Natalia: It's fine. So, we are going to return, and here, it will be a small function called h, and here it should be imported. Okay. So, what h is, it's creating a virtual node, and it's just for usability. It looks a lot more verbose than it was previously, but we want to provide Apollo client, and if you work with Apollo client, it's always like some additional setup. So let's try to run it and give our console errors.
Jason: Okay. So I'm serving, and let's take a look at what happens. Okay. So, so far, no errors. Reloading the page --
Natalia: Oh, no errors, nice.
Jason: No errors, okay.
Natalia: It's magic. When you are doing live coding, it always has errors. Apparently, Jason doesn't.
Jason: Okay. So should we take this part out, do you think?
Natalia: Yes, I think we can remove the query safely.
Jason: Okay. So we can get rid of that part.
Natalia: So, so far, so good. I mean, the app is rendered. We have no console errors, so let's start working with queries and get our errors back, because we will. I have been experimenting, so it's fine.
So let's go -- no, let's not go. Let's first of all allow our web pack to impact queries from GQL files, because you don't want to write bit query every single time using GQL, and for this, we need to create a configuration file, because in Vue if you want to work with web pack and configure web pack, you would need to create a file called Vue.config.JS, and you can see even in the icon here, it is kind of like, yeah, it is smart enough to say this is a Vue config, and here I'm going to cheat, honestly, because every single time I'm searching for this config, and every single time I'm finding it back in my old projects, I just copy/paste, and I will explain. So, this is basically a web pack config. It's not Vue related at all, and we are seeing that if you see files with an extension GraphQL or GQL, please use the GraphQL tag/loader, and make them be able to import and use in JS files, and that's it.
For this, we are also going to create a new folder. That's how I usually do this. I call it GraphQL, so it's very visible, and let's create a new GQL file here. Let's call it allwines.query.GQL. Again, you are free to name them however you prefer, but with this convention, first is the name of the query, and then you define if it's query, if it's mutation, if it's fragment, because you can have fragments with GraphQL or subscription, and then you actually have an extension. I am not sure if it works for everyone, but I think it's useful if you want to like see them very discrete, long names but discrete. So let's copy/paste our query for all wines here.
Jason: Okay. I'll just grab this out of here. Okay. And we can be descriptive up here.
Natalia: And let's give it a name, because I have like OCD about this. Apollo dev tools complains to you every single time you use nonnamed queries.
Okay, okay, it's fine. It's named now. I am happy about it.
Now we have this, and let's go to our app.Vue, and let's try to make the query from the Vue component using Vue --
Jason: Yes, this is the part that I'm really excited about, because this is the part where -- I feel like this is where the light bulb goes on, right? So before we do this, there's one question in the chat. Would this setup work with VIIT (phonetic)? I hope I'm pronouncing it correctly.
Natalia: Yes, you are correct, and I have no idea if it will work with VIIT because we'll be hacking this one. I have seen some issues regarding VIIT, so I guess it works the same way, and Vue Apollo is not ready for both, and if you check issues with Vue Apollo repository, there is something that people refer to there where it's not working with VIIT but with this small patch, where you can make it work, so yeah, it should be fine.
So, let's go to our app Vue, and let's add a very new thing. So, basically, what we have here is a typical Vue single file component, where we have a template part, a script part, and style part, and we don't care about CSS.
Jason: And I think like this is one of the things, where if you're not familiar with Vue, this is probably the most kind of Vue-specific thing that I have seen, is this setup. This is something that seems really unique to Vue, and it's something that's incredibly powerful, too. Like, I love this idea that as I am working on my components, everything lives in the same place but isn't some kind of very specific syntax. So I personally don't love CSS and JS.
Natalia: Me neither.
Jason: But I do really love having my styles co-located with the things happening on the page, so this approach is really, really nice, where I just get to write regular CSS but it's in the same page. So, I think this is really unique. This is one of the things that I love most about Vue, is this approach to organizing code.
Natalia: But also, it's a bit limiting, because you need to really work on your components not to grow, to be incredibly big; otherwise, you're just scrolling all the time, back and forth.
Jason: Fair enough.
Natalia: So, basically, what you have here, what you see here, it's called options API. It's an alt syntax -- I shouldn't say alt. It's one of the current syntaxes as well, and it's still a default syntax. So, what you can have here for example is data, and here in data, you usually define any reactive properties that you're going to work with. Let's say message. Okay, hello, Jason. And you can change it reactively. You can show it here on your template, and there is one of the differences between Vue 2 and Vue 3. In Vue 2, you would need to have only one root node, so I would need to wrap everything in -- right now, we can work with multiple, and you can just show your reactive data here, for example.
So, if we go and check our website, there will be a message right below everything, and it's shown here. So, this is basically options API. You can have methods here, which are basically methods to change anything, to run like executive functions. You can have computed properties, which are getters, mostly getters based on reactive data and so on. But let's also play with the composition API. So, let me first change our familiar data to something new, and this is called setup.
Natalia: Setup is a new option that is present in the composition API. In terms of lifecycle hooks, it's running first before the component is created. So, if I create something in setup, it will be available everywhere in the options API, like data or computed on methods, but I cannot access data from within. Our message simply doesn't exist the moment it is validated.
Let's move message here to show how it works so for our viewers, it's not as cryptic. I will create const message, and here, we will make a reactive property, based on the string hello Jason as an value. So, for this, Vue 3 is completely shapeable. We will be importing every method, and here I'll be importing ref from Vue. What ref is doing, I will be calling it as a function with hello Jason stream, and I'll be removing this from data now.
What's going on under the hood? Ref will be converted. I'm showing how it looks like. This will contain an object with a single value property, which will be equal to hello Jason string. You may ask why is it converting to an object.
Vue 3 is built with proxy-based activity and to make all of the reactivity track with proxy, you need an object, because you proxy find objects there, not primitive values. That's why any primitive is converted to an object, and this object is tracked reactively.
Inside of this setup function, if I want to change anything of the message, I would need to use message value, because this is an object. But here, in the template, it's automatically unwrapped for you. Let's return --
Jason: And so we just return it?
Natalia: Yes, we need to return it.
Jason: Got it. Okay.
Natalia: So we are returning from setup, and right now, it's available everywhere in the options API, as well as it is in our template. So, if you check your website right now, I mean, if I didn't make any kind of mistake, there should be hello Jason string again.
So, this is what we are going to work with. We'll be using a few functions from Vue Apollo composition API. Now we know that we need to work within setup. Here's a syntax that's more familiar to our React friends. What I'm going to do is I'm going to call a query here. We are going to use our all wines query and make an API call from within setup. To do this, I need to import method and use useQuery from Vue/Apollo-composable. Why is it not autocompleting for me?
Jason: This is my setup betraying you. Sorry ( laughter ).
Natalia: I was almost sure I typed it correctly.
Natalia: I really hope for it. It's like I cannot cheat with Jason's setup. So, we have useQuery here, and now we are going to make an API call. So, I want to have some result, and I want to call a correct QL query, and I need to import my all wines query GQL. So let's go here, import -- oh, I'm sorry, import all lines query from -- and let's just... oops. So, this should be correct.
Jason: Okay, so it does look like -- unexpected token. Do I need to restart it after we did the --
Natalia: Yes, yes, because we changed web packs setup.
Jason: Okay, stop and restart it, and hopefully that will clear this up for us. There we go. Okay.
Natalia: Okay, so it's not complaining about the setup anymore, but let's go to our browser and check the console there. I just used query. I just want to see --
Jason: Do we -- okay. Web socket connection. Let me just reload here.
Okay. Here we go. Cannot read property Apollo app tracking of undefined.
Natalia: Yes, exactly, and this is the problem I hit when I was preparing for that podcast. I was like, yeah, let's just make it work. And like, okay, let's search GitHub issues.
Jason: Oh, no ( laughter ).
Natalia: And it's like, we want to make it work, so this one is reported already to Vue Apollo I think three times, and there are pull requests created to fix it. They are just ready to merge, and they are waiting for the maintainer to do this, but they are not merged. And I was like, what do we do in this case?
And there is a way, a super hacky way. Let's make it work anyway. So, what we are going to do here, we will create node script.
Natalia: That will just run and change a few of node modules -- actually, change Vue Apollo node modules with all of the fixes that I mentioned on this pull request.
Natalia: So, bear with me. Let's create one more folder here, not on this. Let's just create a new folder right here. Let's name it scripts.
So, if you are going to share this repository with your viewers, people could easily find it. Let's create a new file. Let's call it fetch.js, and I will just copy/paste all of the things here. Don't even try to dive into it ( laughter ). It's very cryptic. It's just reading -- it's basically just reading load and tracking GS and useQuery.JS. It's in particular compatible with composition API plug in, so it uses a few things from alt syntax. So it uses dollar sign root, and I believe Jason has this in the console, and it's like it's complaining about dollar sign root. We are going to replace it with root. So, what do we want to do here now? We are going to just run scripts fetch js.
Jason: Okay. Do I need to run this locally?
Natalia: Yes, please, try, because for me, it's like I cannot find this module.
Jason: Okay. So I'm going to make this executable, and then I'm going to run scripts patch.js.
Jason: How dare you! I mean, let's see.
Natalia: You are the master there.
Jason: I know, this is my machine. What are you trying to tell me here?
Read write, I said to execute. Chmod, oh, did I do minus X? I'll bet I did. Plus X, scripts patch, and then let's try that again.
Syntax error, oh, we have a bonus -- no, wait, no we don't. Line one, const fs require fs?
Jason: Oh, maybe it... okay. That ran. I ran it as an actual node script.
Jason: Okay. So let's try this again. Oh, you know what it was, we -- I -- yeah, if we had put like the shebang to identify it as a node script, that would have been different. So I was doing it one way, and you had written it as a different way, so that was just me not understanding.
So, now, if I rerun, let me clear this, run it again -- invariant violation. Expecting a parsed GraphQL document. I think I know what this is. I think we forgot to put our all wines query into the use query. Let's try that again. Hey, hey, no errors.
Natalia: Awesome. At least it's not breaking. It won't show everywhere right now, because we didn't return it, but if you check the console for GraphQL calls, there should be a GraphQL call for our API.
Jason: And we can check that.
Natalia: Refresh it, because we are running on component calls, and check if there's any GraphQL calls. Excellent.
Jason: Okay. So we have got our data. Now, let me see if I can do some Vue. I'm going to try something.
Natalia: Okay ( laughter ).
Jason: If I, knowing what I know based on what you did with the setup function, if I want to store my wines as a value, then I would do const wines equals ref, and then I could do result, data, all wines. And then I can return them like this.
Natalia: Oh, try it and I will say what is wrong here.
Jason: Okay. So I'm going to try to use this, and we are just going to see what happens if I try to dump all of this data. And it explodes, and it exploded because cannot read property allWines of undefined.
Jason: Oh, because the query has to finish.
Natalia: Query is finished, but we are not -- so, first of all, let me say, first of all, result is already a ref, because Vue Apollo is doing this for you.
Jason: Oh, okay, okay. So I didn't need this then.
Natalia: That's the first part, yes.
And also, to unpack a result, Vue Apollo has a specific function called use result. I mean, you can use it as it is right now, but the problem is why it complains about data allWines, because your result is a ref, and it has a value. This is an object that contains a value property, so if you console log result -- like, let's console log the bug that's happening.
Jason: Okay. So we'll console log result, and I'm going to reload the page. Oh, no. Wine does not -- oh, I have got to get this out of there.
Okay. So, here's our ref. Value, allWines.
Okay, okay. So, if I -- do I -- if I want to use allWines, I can just like return result like this? Or how would you -- what would be the right way, if like I wanted my list of wines?
Natalia: I think my internet is dying again.
Jason: Oh no! I thought you were just like unimpressed with my question ( laughter ).
Natalia: ( Laughter ) no, it was a really nice attempt. I'm sorry for my facial expression. It was internet.
So, you are already on a good track, but let's use some magic that Apollo client allows us to use. So, let's first explain what we are doing here, because we made a query. Right now, we are using query, and people probably think like, okay, it's a promise. The thing here is not a promise.
Natalia: Because Apollo has two different types of queries. First is query, which we used previously. It's just completely like you use fetch or axios. It's just a result, you have a result, thanks, bye.
But Apollo also has smart query, which creates an observer, and smart query, it's like you're subscribing to this one, and it's reactive. If the result of the query changes, your application reacts to it. They have their own implementation observables. They have their own implementation called Zen observable, if I'm correct, but you don't need to dive into this. You just need to realize that smart query is -- Here, we have a smart query, and with a smart query, we can define different options. With update, you could say I don't need all of these huge objects I just fetched. I don't need data rest or whatever. I just want to have allWines. Maybe I want to specify a different name for the property, because Apollo client is very particular about this. If you have allWines in property, you need to have allWines in your response. Otherwise, it's not matching. In Vue Apollo, we have this other thing. Let's just simplify a bit. So we will create a const called -- we can say wines, and we will useResult -- no alt for you, Natalia. Go and edit yourself. It's like a nice thing, you, as a developer, just use it.
Jason: It's amazing, working on this VS code setup versus my day to day, I realize how much I depend on VS code to solve my problems for me, but I like it this way, because the automatic stuff, we have to talk through it. Otherwise, I watch people who are so fast with their auto completes and stuff, I have no idea what --
Natalia: Alt imports, they write some stuff we just alt imported for you. And it's like, whenever this came from -- so, some cute stuff.
But, okay, so we are going to use result. It is a function to expose the property that you want and to define a default value for this, because what if result is not defined? What if we failed with use query? So, we are unpacking result here, and just in case, you can always -- with something more intuitive here. But for now, we have result, and we are happy about it. We will give it a default property of null. So, if something went wrong, at least now it's specified as wines. So, our Vue application is not complaining at us, saying like what the hell is wines, I have no idea.
Jason: Got it, uh-huh.
Natalia: And here, we want to expose our data. I think it's --
Natalia: So, now we don't need console log result, and we can return wines here.
Jason: Great. So, then, up in the top, when we do this list, theoretically speaking -- yeah, look at it go! Hey, we just GraphQL'd in Vue.
Natalia: Yeah, we hacked it a bit. Good thing that we --
Jason: So, to maybe dig in a little bit to that hack, the hack is only to implement changes that just haven't been merged yet. So, this isn't like doing something that won't be fixed. It's getting a fix in that's not available yet.
Natalia: Yeah. We just -- and we are just merging this page ourselves.
Natalia: And I really like this kind of hacky stuff. I always feel like, I don't know, super hacker. I just made a thing work, and if it doesn't work as NPM modules -- so the good thing is we have wines, and we can actually make some Vue magic to render this list in a bit better shape than we do right now.
Jason: I would love to see that, yeah.
Natalia: Because right now we are just rendering this stuff. So let's be nice. Let's create a list. Let's use semantic HTML, because this is important.
Jason: And then I can update that -- oh, you already added it. Okay, great.
Natalia: Yeah, I added it. Now, please -- a wine title for every single list element.
Jason: Okay. So we can add like an h2, we'll go wine.title.
Jason: And then I'm going to get rid of this big JSON dump up here. Look at it go.
And there's one comment in the chat that I do want to address. Orlove, we used a node script to get it to render, so what this node script is doing, to be clear, you won't use this once the PRs are merged into the Vue Apollo repo. So what we are doing here is we are just applying a patch. We are basically applying a PR to the ship node modules. Once the pull request is merged, these changes will already be in the code, so we won't have to run the patch. This is one of those things that like in the near future, this patch.js won't be necessary. It will just work because those PRs will be merged.
Natalia: We are just using the hack for now. If you have an open source maintainer, it's very hard to deal with all of the pressure and all of the stuff that you need to do, and it's not your work. You're not paid for this. Let's not complain about the maintainer. It will be merged for sure. He is a Vue core team member.
Remember, you are not obliged to use the latest version of everything. We are experimenting here, because everybody would like to explore Vue client 3 and Apollo client 3. It's new for me. Usually for production right now you will be using Apollo client 2.6 mostly. I'm using it as well. I know it.
But we are just looking a bit forward, looking into the future, and just trying to show how things will look in probably half a year, hopefully.
So, we have a query. Everything is good. I am just checking. We don't have must have time left to create a new wine. It's very tempting to create a new wine and add it to the list. But let's at least delete some kind of wine. We can work on deletion.
So, for this, we will be using mutation, obviously.
Jason: Maybe we could just get the UI in place, and we could say delete this wine.
Jason: So, for each one now, there's going to be a delete button.
Natalia: Yes, and what we need to do here, so, obviously, in Vue options API, you would be creating a method, which would be run when you click this button. We use Apollo, and Vue composable, we will be using mutation hook -- I shouldn't say hook. It's so React-ish. Use Apollo composable use mutation, but if you work with React Apollo client, it has a hook called use mutation, so it's very similar in these terms.
So, let's import useMutation from composable. I'm getting to this like, okay, and we will create a new function here.
So, let me use mutation. It will expose a method called mutate. So whenever mutation is called, you're mutating something. But honestly, you shouldn't use it with this name in the application, because what if you have five mutations and every one is supposed to mutate? Let's call it delete wine. Right? And then we are going to use useMutation, but we need to create a mutation. So let's go to our API, and this is like Jason work, not mine. Let's go to our Prisma play ground and check what we have.
Jason: I can delete a wine, and it wants an ID, and --
Natalia: That's why we were smart. We were fetching an ID.
Jason: And then this, it doesn't return anything when it comes back.
Natalia: Yeah, we don't need to return anything. It's fine. I was lazy when creating Apollo server. It's like, delete it and forget about it.
Jason: So, probably then, we want to set this up as a --
Natalia: As a mutation, as a GQL file.
Jason: And do you want to set it up as a variable, right?
Natalia: Yes, let's show variables. We have 20 minutes. Let's play with variables.
Either way, we have -- so this is kind of what's going to happen, right? So we get back a true that the deletion was successful, and any wine that we pass with this ID will get dropped out of our configuration. So, we can see here, here are our actual lines, and so I'm not going to delete one from here, but we could just take this as the ID and drop it in here, and that would delete this wine. It would stop showing up.
But so what I'm going to do instead is let's go into our GraphQL, and I'm going to say delete wine.mutation.
Natalia: See, you are getting the naming convention real fast.
Jason: It's a great naming convention. It's super clear. There are no questions about what's happening in here. And I guess I will name this as well, so we'll call it delete wine, and that will be our GraphQL file, so then over here, I can import --
Natalia: Import it, yeah.
Jason: -- delete wine mutation from --
Natalia: See, again, you have got the naming convention even here. It is so nice to see.
Jason: Delete wine mutation.GQL.
Natalia: I really honestly want to hire Jason. It's nice to see a teammate that gets every naming convention without explaining it.
Jason: Ooh, but I spelled it wrong. There we go. ( Laughter ).
Natalia: This is fine. This we all do.
Jason: So this then should put us in pretty good shape. We have got a delete wine, and then should I return this straight up?
Natalia: Of course, you should return this.
Jason: This is the part where I don't know enough Vue to know what to do next.
(Brief pause in captions.)
Natalia: It should be an object, because we have ID. So, our ID here is actually wine ID, yeah, correct.
Jason: Okay. So this, then, theoretically should work.
Natalia: Let's go and check. What if we failed? I mean, I don't know. I don't know. I can only guess.
Jason: So, our first wine here is this Samuel Tinon, so let's delete it.
Natalia: And let's check our console, and let's check our GraphQL. This is important. Right?
Jason: Okay. So, I should have had this open when we started. Oh, wait, it's gone.
Natalia: Click on delete this wine again, right now, the one you have open. It's not gone. You deleted it. It's correct. GraphQL call is successful, you can see this, and if you refresh a page, it's gone. There is one very important part missing right now.
Jason: Okay, so I just deleted this one. So I refreshed the page, and it's gone.
Natalia: And it will disappear, yes.
Natalia: What's going on here and why is our wine not disappearing immediately? There is a thing about Apollo cache. So, let's dive into it a bit for people who have never used Apollo.
Apollo itself is not only a GraphQL client. It's not ( off mic ) for GraphQL. Apollo also has a cache, and when we were creating a client, we specified it as cache in memory cache. You probably have seen this, and what is cache doing? Every single time you fetch something from GraphQL end point, it's stored in the Apollo cache, and on any subsequent call, when you are querying absolutely the same query, with the same variables, Apollo will first check the cache, so you can think about it as a getter. You have a query. First it's like, okay, if it's in the cache, cute, you have it in the component. If it's not in the cache, it's going to fetch it from the GraphQL end point. That's how Apollo is also capturing the local state of the application. It's possible to store there even things that do not exist on the GraphQL API at all, but right now, the important thing is Apollo is smart, and when you are updating a single entity in the cache, it's smart to check by ID and update it. So if we were updating the wine, changing its name, it would do the refresh for us.
Natalia: The problem here is when we add an entity or delete an entity, because in this case, we need to explicitly tell it, hey, we changed the list. Please update the cache for us.
Natalia: That's why Apollo contains also update functions. So when we are specifying any kind of mutation, any kind of query, like creating or deleting an entity, we can also specify an update hook. And in this case, it's a bit more problematic, because we will need to specify this hook in the function on the template, and you can quickly check for the syntax, because, again, with Apollo client 3, I don't know the syntax of the update hooks. So, give me a sec to check documentation. I know we are short on time, but it's better to do it correctly from the start.
Jason: And maybe we can actually just define the function here.
Natalia: You mean like delete wine?
Jason: Delete and update with an ID, and then we can run --
Natalia: Yeah, we can run delete wine here as well. Meanwhile, let me... and it's really fun when you are coding without like knowing if it works or not. Usually, when you do live coding, at the presentation at conference talks, you kind of know what is going to happen here.
Jason: That's kind of the ethos of Learn with Jason. It's loosely controlled chaos ( laughter ).
Natalia: It's so much fun. You can't deny it, right?
Jason: I mean, every time I do this show, I leave and my cheeks hurt from smiling. I have laughed a bunch. So the highlight of my week are Tuesdays and Thursdays when I get to do the show.
Natalia: I'll bet. Okay. We can define it on the use mutation, which is good.
Jason: Oh, we can define it in here?
Natalia: Yeah, but we can also define it -- I'll quickly check in this form, but we can also define it in the function.
Sorry for the silence. I'm just trying to find out how to operate better with this.
Jason: No worries. In fact, I think we even have -- we can just like do a little bit of music while we wait.
Natalia: Oh. ( Laughter ).
Jason: I'm so happy I unbroke my sound effects. They have been broken for two weeks, so this really just warms my heart.
Natalia: So, if we want to define anything on the mutation, like update hook, it's going to the callback function, so, like -- it's like this.
Natalia: And we can specify update hook here. It's like update, but, I mean, logic is going to be here, with like all of the reading query and writing query as well.
Jason: Do we need to return an object, or is this --
Natalia: No, no, no. We don't need to return. This is not an object. This will be -- let me again do a quick recheck on this. Yeah, we need to return an object, sorry, because this is a configuration one.
Jason: Okay. So should we just do it like one of these kind of deals?
Natalia: So the thing right now, I'm trying to remember, how do we pass an ID here, because what we can have in our brackets and in update, basically, is actually only the store, which is Apollo cache, and everything that we get as a response -- so, let's try to play around with this.
Jason: Oh, interesting.
Natalia: Yeah, exactly. I just realized that sometimes I used update, and you could use it externally, so there is a chance we can try to run it in deleteWine. Okay. So let's try to see.
First of all, I will just trying to get the ID here to see if callback receives an argument. And let's play around with update.
Natalia: So what we have here in the update, it's a function, first of all. And it accepts cache store as a first argument. So, this is basically our Apollo cache here.
Jason: Got it.
Natalia: And the second one is whatever we get from -- so, here, if we would be returning anything from the API, like some object with ID of the wine, we believe it should be here.
Jason: Oh. What if we just update the server to return the ID instead of returning a Boolean?
Natalia: I am not sure -- oh, you want to return back server. With just five minutes.
Jason: Yeah, maybe not with five minutes. Maybe we don't do that.
Natalia: It's really nice. So let's -- what we'll do here, let's just try first of all to read. So, when you want to update anything in the -- in the case of the example, let's try to at least show how we'd read this store and how we'd change it. If we are not successful with an ID, at least we can show how it operates with a store.
Natalia: So we will have const data, which is store read query, because Apollo cache is operating with queries all the time.
Jason: Uh-huh, and that's kind of like it uses the query as the unique identifier.
Natalia: Exactly, so we want to retrieve whatever we fetched as all wines. Query is allWinesQuery.
Natalia: And let's console log it. And we'll check whatever is going on when we delete a wine. Not so many wines left.
Jason: We still have a decent amount of wine left, so let's delete one and see what happens.
Okay. Cannot read property ID -- so, this one, it didn't like that. What if we get all data out and see what comes back? So then we can just kind of like log both of these and see what it gives us when we try to --
Natalia: I think it will be nothing, but let's check.
Jason: Okay. So our arguments are empty B you the data is the cache. We get all of our wine, and it does include the one that was just deleted.
Jason: Yeah, so it looks like --
Natalia: It looks like we updated properly, in this case, and okay, I'm just trying to get a few -- if we can do it on deleting wine like with a second parameter here, so, this is -- basically, this is variables.
Natalia: Here, and let me quickly check the API, and I remember that you could pass some previously updated here, so we can do this as well. I am not sure if they have updated the documentation. I was complaining about this one. I remember this. The mutation, cache, cache updates -- yep, this is cute. And message variables... uh-huh...
So, yeah, you can pass it as a second parameter as well.
Jason: Oh, cool. Okay.
Natalia: So whatever we have here, we can just copy/paste our things, and -- whoa, what have I done? Okay. I just copy/pasted, and we can delete the second parameter all together here, and we can pass it here.
Jason: Excellent. Okay.
Natalia: So right now, let's try it again just without args. No more args for us. And please delete the wine for me.
Jason: Okay. I'm now in -- here's our app. I'm going to reload the page. I'm going to delete this one, and nothing happened.
Natalia: Ooh. And we are calling delete -- oh, we are calling it delete wine. Sorry.
Jason: Oh, yeah. So we need to return delete and update cache, and we'll change this one to be delete and update cache, and then I made this one to just the ID gets sent in, so we'll update that.
Okay. So let's try this one more time. Here's our data. We have got our wines. Okay.
Natalia: Okay. So it console logged it. So we are almost here. So our update function is running, which is already good, right?
Jason: Yes, yes.
Natalia: All right. So what we are going to do here, and this is a huge thing, I think, and I think Apollo client 3 will scold me for this right now, because I'm going to mutate the cache, and this is not how we do this. So const data is -- okay. I will try to make it as immutable as possible. This is an important thing about Apollo client 3. You can mutate a cache only in a mutable way.
So I'm going to do this like that.
Jason: Oh, okay, okay.
Natalia: Let's create updated data, usually using libraries like -- if you have a deep nested structure, but here we don't have a deep nested structure, so we are fine with this. So we'll be using data filtered, right?
Jason: It's going to be data.allWines.filter.
Natalia: Sorry. For me, it's already 10:00 p.m., and my brain is -- So, we want to return everything if the ID is not equal to the ID that we have here. So we want to exclude the ones with a given ID, and the rest of the stuff is fine.
Jason: And then do we just need to return --
Natalia: No, no, no, no return.
Jason: Oh, sorry, sorry.
Natalia: We need to write it back to the cache. With the data, we changed something, and now we are going to do store writeQuery, and, again, query is the same. We are going to change all wines query here, but we also need to pass data, and data will be our updating data.
Natalia: Let's try it.
Jason: And I think we have to do all wines, because when we dug down here, we ended up with this updated data ends up being this array. So that, I think, that should get us, and if it doesn't, we might have to -- let's see. Come on, fingers crossed, everybody, drum roll.
Ha-ha-ha! Look it go! Beautiful.
Natalia: And it complained a bit, because we have a variant violation. I can't read it, but I guess that is complaining about the different structure --
Jason: The custom merge function. Okay. So, this would be something that we definitely don't have time for today, but --
Natalia: Yeah, and it's new for us, both, I think, because it's Apollo client 3. But at least the interface is updated.
Jason: Yes, and so we -- we managed to get this right. This puts us in really good shape, and that, I think, is a great point to call this done. So, Natalia, thank you so much for your time today. Remember to go follow Natalia on Twitter.
Holy buckets, did that just work?
Jason: Hell, yeah, it worked. So here is your website. Anything that you want people to go check out?
Natalia: Vue 3 documentation. If you don't use Vue, it would be great if you could read the documentation, try it, and if something is wrong in the docs, which can happen, please submit it as an issue, because that's the only way that we can improve documentation.
Jason: Okay. Absolutely. With that, one more shout-out to our sponsors. Thank you White Coat Captioning for making this show accessible to more people. Thank you to Sanity, Netlify, and Auth0 for making that captioning possible and sponsoring the show.
I had a blast, really excited about what's becoming possible for Vue and Apollo. We have a lot of fun stuff coming up. Later this week, we are going to look at building some static sites with Sanity, and that's going to be a lot of fun. So make sure you come check that out.
Stay tuned, chat. We are going to raid. Natalia, thank you again. We will see you next time.
Natalia: Thank you.