DejaVue

Data fetching is a crucial part of any web application. In this episode of DejaVue, we discuss the different ways to fetch data in Vue.js and Nuxt.js, and how to cache it. 

We also talk about the experimental Suspense feature in Vue.js and how it can be used to improve the user experience, as well as how to handle third-party scripts in your application.

In addition, Nuxt's data fetching options are discussed, including the $fetch method, useFetch, useAsyncData and the useNuxtData composable. Finally, we cover server-side caching in Nuxt.js, including route rules, defineCachedEventHandler, and defineCachedFunction.

Enjoy the episode! 

Chapters

  • (00:00) - Welcome to DejaVue
  • (00:50) - Which topics should be covered next?
  • (01:56) - This episodes topic - Data Fetching
  • (04:13) - Two ways of client-side caching
  • (06:17) - What is Data Fetching actually - and which library to use?
  • (08:19) - Suspense in Vue.js
  • (14:03) - Third Party Scripts
  • (17:38) - Data fetching basics in Nuxt.js
  • (20:28) - $fetch vs useFetch
  • (23:27) - $fetch request deduplication on the server
  • (24:43) - Caching in Nuxt.js
  • (28:18) - routeRules of server-side caching
  • (29:38) - defineCachedEventHandler / defineCachedFunction
  • (32:19) - Unstorage for storing the cached files
  • (35:41) - useNuxtData
  • (40:26) - Why does Nuxt offer more data fetching options?
  • (45:32) - Wrapping up

Links and Resources



Your Hosts

Alexander Lichter

Michael Thiessen

Links marked with * are affiliate links. We get a small commission when you register for the service through our link. This helps us to keep the podcast running. We only include affiliate links for services mentioned in the episode or that we use ourselves.

Creators & Guests

Host
Alexander Lichter
Web Engineering Consultant • Founder • Nuxt team • Speaker
Host
Michael Thiessen
Full-time Vue educator
Editor
Niki Brandner
Sound Engineer

What is DejaVue?

Welcome to DejaVue, the Vue podcast you didn't know you needed until now! Join Michael Thiessen and Alexander Lichter on a thrilling journey through the world of Vue and Nuxt.

Get ready for weekly episodes packed with insights, updates, and deep dives into everything Vue-related. From component libraries to best practices, and beyond, they've got you covered.

Michael Thiessen:

Welcome to DejaVue.

Alexander Lichter:

It's your favorite Vue podcast. You just don't know it yet, or maybe you do. Well, we are weighing our 30-somethingsth episode, and, with me here is Michael Thiessen. Michael, how are you doing?

Michael Thiessen:

I'm doing pretty good. And I'm here with Alex, who's got an interesting backdrop this week. 10,000 subscriber special around this time. So you may have already seen it on his on his channel. But, yeah, congrats on that, by the way.

Alexander Lichter:

Thank you so much. Yeah. I mean, like, in a year and, I don't know, a month and something, I I didn't expect to to grow that fast. I'm very happy that more important than the number than, like, people enjoy the content, be it the podcast here, of course, the YouTube channel, a blog post if I post once a year or something. So, yeah, re really happy to see that.

Alexander Lichter:

And, I'm I'm super curious if if anyone out there for the podcast but also for the channel has some ideas like, hey. What should we talk about? I've seen, like, people commenting, hey. More like enterprise and Vue topics maybe, or can we talk about linting? Or is that just drop it in the comments, send it to us on on Blue Sky, on Twitter, wherever you are at.

Alexander Lichter:

Right? I mean, nowadays, every rep dev moved over to Blue Sky, it seems, so get an account there and and ride us. Yeah, 10 k. Looking looking forward to that. The special for it will come out in in the future just because, yeah, didn't have that much time to prepare it yet.

Alexander Lichter:

But we recorded a few things, so stay tuned for that.

Michael Thiessen:

Yeah. You've been busy with conferences and flying around and all of that. So

Alexander Lichter:

Yes. But I'm also I'm so happy to just, be home for a little bit. I'm looking forward to, like, livestream here and there. It's also something I really would like to do more just because I'm traveling for, like, yes, set workshops and conferences. It's sometimes really tricky, especially, like, having a consistent schedule is not really possible with, like, videos to podcast.

Alexander Lichter:

It's a bit easier. You can record beforehand and all that. So yeah. But, I mean, that's that's fine.

Alexander Lichter:

Sometimes you should do things asynchronously like, fetching your data.

Alexander Lichter:

Right? Yeah. Which is the topic of of this week's episode of DejaVue, data fetching in in Vue.js and also Nuxt.js.

Michael Thiessen:

Yeah. There's it's a it's a big topic, and so we're gonna cover cover how that how that works, the client side more focused with Vue, and then, you know, introducing that server side bit with with Nuxt. Obviously, you can do server side with just Vue, but, you know, we're kind of split it out in that way because it's kind of a nice, a nice organization. So data fetching, that's like one of the the main things that we do in applications is fetch data. And it has a big impact on performance because the network is usually the bottleneck for our for whatever we're doing.

Michael Thiessen:

And it can get really complicated when you've got a big app that has, you know, one component in one spot is fetching data in one way and then some other components fetching other kind of data and wants different piece of data and, like, organizing at all. You know, we have a whole state management piece that goes along with that. But then just like the network calls and everything, and how do we cache that data? Should we cache that data? All of that.

Michael Thiessen:

It's a whole lot of a whole lot of stuff to to figure out and to think about.

Alexander Lichter:

Yeah. Also, like, think about how should I fetch the same data twice? Should I reuse it? How to reuse it? Right?

Alexander Lichter:

The the caching part there as well. And as I manage, like, state management, waterfalls, so, like, can I fetch data in parallel, or does data a have to wait and data b? And, yeah, that's that's all complex. And as you also mentioned in our performance episode, like and as you just said, Michael, the the framework is rarely the bottleneck. It's often just the API calls, which take way longer than your framework rendering, I don't know, list of 1,000 entries, which you probably shouldn't do anyway in most cases.

Alexander Lichter:

But, yeah, that's the case. So I'm curious what we'll see here. I mean, we all did data fetching our application so far. I think there is almost no application out there that doesn't fetch data, be it even from things like local storage or just like state from somewhere, but especially from APIs, be it third party or own APIs.

Michael Thiessen:

Yeah. Exactly. And, yeah. So to get started, we've got basically, like, 2 ways of of dealing with caching. And so the first one is that the user has some some native caching of of your your HTTP requests based on, like, different headers and and things like that.

Michael Thiessen:

And so, you know, you've seen this with with images and other resources that get fetched. If it's the same thing, the browser automatically handles that, and we get the status code back, and we we can make sure that we're not gonna fetch that data multiple times if it hasn't changed. And so that's the most basic layer, and we don't really think about that because we don't need to, and that's really great. The other layer is I only use this sometimes, but I'm wondering if I should maybe implement this more. Is that there's this, like, the the session and local storage Mhmm.

Michael Thiessen:

Where you have, like, a bit more explicit control over saving things to to the browser itself. And so, you know, different apps like, I don't know, like drawing apps and other things like that, you you know, they often do local storage first. Maybe they don't even there are some apps that don't even save anything to a database, and it's all locally stored. And I think that's a really interesting pattern. I don't know if we wanna go on that tangent of, like, you know, local first versus saving it to a a database somewhere in the cloud.

Michael Thiessen:

But, yeah, that's that that's another level of of caching that we've got.

Alexander Lichter:

Absolutely. Well, as you said, with HTTP headers, just as long as server is sending the right headers, then we don't have to do anything as as front end people, consuming the API. But, of course, if it's not the case or if it's a 3rd party API or if we just say, like, okay, we get some random content and we just wanna store that and don't wanna refetch it, then we, of course, can save it in memory. But then if you refresh the page, it's gone. So local and session storage is is the way to go there.

Alexander Lichter:

But maybe we can even go and step back and think about, okay, data fetching, in a very simple terms, like, what does it entail? Like, how do we do that actually? And, I mean, luckily, Vue as a framework doesn't provide a fetching library. And now it's maybe weird so luckily because, like, oh, yeah. Why isn't that that a good thing or a bad thing?

Alexander Lichter:

Well, you have the freedom to use whatever fetching library you wanna use. Right? We have the native fetch API, which is in the browser. That's nowadays, like, a modern way to use. Probably not, vanilla.

Alexander Lichter:

I mean, you can do that, but then you have to handle, oh, is the response okay? Still have, like, try catch. You have to maybe convert it to JSON, so on and so on. You can use something like, ofetch or also in Nuxt.js, it's auto imported as $fetch or any other fetch API wrapper, like ky, for example.

Alexander Lichter:

And that's basically just the fetch API with a tiny bit of overhead. It's usually, like, smaller than a KB or so. And that, will will simply work by giving you some tiny helper methods that make your code easier to read. They will make your code also a bit more elegant because you don't have to do these always low level checks. For example, automatically throw in an error when the response is not okay.

Alexander Lichter:

You might wonder, oh, why it's not that a fetch API specs? But there there are good reasons for that, though. These wrappers, the helpers allow it to just do that straight away. And, of course, in the past, people have used Axios, which is also fine, but I wouldn't recommend it for, like, newer projects, of course, or the good old XML HTTP request. Right?

Alexander Lichter:

Back in times where we didn't have anything else, or like the Jake very abstraction around it. So, if you want to use anything like that, or even a server state management library, like, 10 stack, for example, you can do that. Like, you're not forced as in other frameworks like Angular to use the HTTP client there because that's the way to go. You can freely choose. And I think that's quite important.

Alexander Lichter:

Okay. And coming from fetching libraries, let's talk a bit more about async state and Vue and how to deal with that. And there is an experimental feature, which is pretty popular and hopefully will be stabilized soon, Michael. So tell us a bit more about, about that.

Michael Thiessen:

Yeah. And in Vue, we've got this suspense set of features, which I haven't actually, like, dug into for a while. I did write, like, this in-depth article on it a few years ago. And it's funny because it's, like, one of those things where if I'm googling for something, how does suspense work? Then I'm probably end up gonna end up back on that article and and be like, wow.

Michael Thiessen:

At one point, I knew this, but now I've kind of gotten rusty because I've been, you know, more focused with, Nuxt and Nuxt kinda handles that under the hood for you. But the basic idea is that components, you don't want them to render or you don't always want them to render until they've got all the data that they need. And so if you put in a wait inside of your script setup and you return that, basically, returning a promise from this setup function, then you can have it set up so that your Vue app won't render that component until it's actually ready to be rendered. Because otherwise, what happens is you get this, you know, data is undefined kind of thing where you're waiting for that data to populate, but it's not yet. So you have to check with flags or something like that to to see, oh, is this data there?

Michael Thiessen:

Oh, it's not there. Okay. Then I'm in a loading state, so let me show the loading spinner. But with suspense, it sort of handles that all for you. You have your main component.

Michael Thiessen:

And while that's being, you know, that data is being fetched and it's the promise is still going, you have it fall back onto your loading state, whether you have a spinner or a skeleton or something else like that.

Alexander Lichter:

Yeah. And it could be a whole component. Right? You can just provide us, like, a loading or even an error component there if you want to for the fallback, which is pretty nice. But, like, I think those pens is really great, as you said, if you have some, like, heavy async operation that you need to do before rendering the component.

Alexander Lichter:

But I also think in a lot of cases, suspense is not necessarily needed and can be also very tricky. So for example, if you just have, like, a call in your component to say, okay, I wanna render some data I received from an API, and that can be like way after component is mounted. Then we started, okay, how's the best way to abstract it? And well, one easy way is just use, VueUse's useFetch. Here we go again, the good old VueUse recommendation.

Alexander Lichter:

Or Yeah. Build build your own composable to say, okay, have a ref. That value is initially something like null. And then whenever the data is fetched, then you just set the ref to the newly value. Ideally that ref can also be a shallow ref because if you only use it for data fetching, well, it's a good use case.

Alexander Lichter:

And we also talked about it in the in the past. But then whenever something happens to that ref, you can react to it. You can watch it. You can render things with v-if based on the ref. And then, for example, pass on the data in a new component that's only rendered when the data is actually there.

Alexander Lichter:

So in a lot of cases, you might not even need suspense at all, plus data loaders, which are a kind of new thing in the Vue router, unplug in. It's also developed, and they're kind of stable by now, made by Eduardo, they also, don't use suspense at all because it has some limitations. So keep in mind that suspense is a great way of of dealing, with asynchronous state, but also just a regular composable, might be even easier.

Michael Thiessen:

Yeah. And we talked about the data loaders and how that works with Eduardo a few episodes back, I think episode 31, but that's gonna be in the show notes if you wanna take a look at that. It's pretty interesting stuff and, yeah, I think it's gonna solve a lot of issues with that people have with with data fetching. One opinion that I have, which I'm very willing to be challenged on, but with suspense is that it's more of those, like, architecture level kind of things where you probably will set it up in, like, a layout or something like that, and it's less something that you'd be dealing with day to day, like, in each feature that you're working on. It should be something that's more like you shouldn't have to worry about it.

Michael Thiessen:

If you set it up in your Vue application, you shouldn't have to worry about it as you're building out new features. It's just sort of like just like with air handling and, like, things like that. It's just sort of like you know, if you have a a full page error or something like that, it kind of bubbles up, and then there's the thing that that you set up 6 months ago that that handles it. And so if you do use suspense, it's maybe not something that you have to to think about or worry about constantly.

Alexander Lichter:

Yeah. At least if it comes to, like okay, as I said, you have, like, your suspense around, like, the component that the Vue router is rendering or as I said before, if Nuxt is just out of the box there. That's true. They just set it up once and and you're good. Though you can also use this pen for, like, more fan grain situation if you want to have, let's say, you have, 5 different widgets on the screen and you don't want a loading state for all of them separately, but you want a loading state until they're all there because they all have information that belongs together, let's say, or just, like, I don't know, 2 components and you don't want to show the the lower one first and then the one that's fetching the data, but just wrap a suspense around both and say, okay, whenever both async components are finished and show them both together and otherwise, show a loading state.

Alexander Lichter:

So sometimes this can also be right really nice to avoid the the the cluttering on the screen. It's like, oh, there, pop something up and there, but it's like everything nice resolving together and is a bit more, let's say, coherent in terms of, appearance. While, of course, that means longer loading times for the user, but sometimes the perceived performance can can even be better there.

Michael Thiessen:

Yeah. Yeah. Good point. Keep things organized instead of having a 1,000,000,000 spinners everywhere.

Alexander Lichter:

Yes. Exactly.

Alexander Lichter:

And, I mean, then from that part, I mean, async state is is super important. So, like, with all the different ways to to solve that, we all talked about caching a little bit. We come slowly in terms of performance.

Alexander Lichter:

And there has been a lot in terms of third party scripts, so not even fetching data but loading third party scripts, which, in a way, is also fetching data but differently. Right? And them being not on the main thread but in, let's say, a web worker. So, for example, there's something like Partytown that you can use to run all these things on the site, not blocking the the main threat, then that means having still a UI that's usable and also better LCP. And, yeah, also, Daniel Roe, from the Nuxt team worked on Nuxt workers, which basically allows you to use web workers for any kind of thing, but usually, like, heavy operations while just having your your main thread clean and simple, let's say, I don't know, image manipulation or whatnot.

Alexander Lichter:

Mhmm.

Michael Thiessen:

Yeah. Service workers are web workers are are something I haven't really dug into a whole lot, and, it's such a such a useful feature. And, you know, we've got things like mock service worker for for testing and and that kind of thing that take advantage of this in a very seamless, like, nice way. If I was doing an image editing app, you know, you gotta put that in the don't wanna don't wanna block the UI to to do all of that kind of stuff or any other sort of heavy lifting. I'm sure there's a lot of different use cases for it that, that are out there.

Alexander Lichter:

Yeah. Especially as you said, like, the the heavy operations. And a good example is squoosh.app. Some might note it also like Fisher notes. So, like, where you just have, like, your, image editing in the browser to, like, compress and minify JPEG, SVGs, and whatnot.

Alexander Lichter:

And you can do that easily. There's no freezing. You can always use the app interactively, and there's some heavy work done in a worker to make sure everything is still smooth and interactive. So, yeah, that's definitely useful if you have that kind of case. And if if you're more concerned, of course, about performance of your 3rd party scripts, then Partytown is an option or also Nuxt scripts, though they both do very different things in terms of performance, Partytown with the web workers, Nuxt scripts by just saying, hey, let's load the script at a later point of view, and or like just a later time point.

Alexander Lichter:

And we just have a proxy in place to make sure if you, I don't know, collect some data beforehand, you can do that. And then we just replay everything that the proxy has as soon as the script is loaded and do lots of other things, whatever you basically want to do.

Michael Thiessen:

Yeah. And as an aside, it's, it's really fascinating to see all the different apps that are that are being built with all the different in browser stuff that people are doing, like photo editing and video editing and games and things like that, and we're getting, it's really interesting to see the the web platform getting closer and closer to to native, and it's like the difference, between, you know, what what can be done on native versus what can can't be done on native is is getting really small. That's a topic for a whole, other podcast, though.

Alexander Lichter:

Yeah. Also, like, Wasm, for example, is, like, it's amazing to see that you can play on Nodeoom and browser and stuff just, like, compiled with Wasm. So, yeah, let's let's see what it brings us. But as you said, it could be a nice, extra episode. If you're interested in that, drop us a comment and and let us know.

Alexander Lichter:

Right? So we'll we'll get someone on who has a lot of FASM experience and see see how that goes.

Michael Thiessen:

Yeah. So the the next part is with Nuxt. And so Nuxt has a few extra things to deal with when it comes to data fetching because we are dealing with server side rendering that then gets hydrated into a client side app. So it's a bit more complicated, and what we've gotta do is first deal with the data fetching side of things. And so the most important one here is to fetch data with the the built in methods, composables that that come with Nuxt.

Michael Thiessen:

So we've got use async data, we've got use fetch, and we've got the lazy versions of those. We also have dollar sign fetch that, Alex mentioned before, which is the ofetch that that gets auto imported for you. And so ofetch is being used under the hood here for for the for the data stuff. And these handle the the hydration life cycle for you so that on the server, it'll fetch the data, and then it'll just pass that along to the client, and we don't have to fetch it on the server. And then once once that gets to your browser, refetch that again because, obviously, that's extra work, and we don't wanna do that.

Michael Thiessen:

And so, yeah, these things sort of they they handle that for you. They then with server side rendering, we also have this issue of cross state request pollution. I always forget the the exact wording. I think I got it right.

Alexander Lichter:

Yeah. That's that's correct.

Michael Thiessen:

Yeah. The one of those jargony terms where, basically, if you've got data fetching happening on your server, while if 10 people come to your site at the same time and those requests are being processed simultaneously, we don't want someone else's data ending up in your request or vice versa. And so we have to do special things to make sure that we isolate every single request, which

Alexander Lichter:

Exactly.

Michael Thiessen:

Doesn't happen by default. And so you don't wanna have that to happen. And so, yeah, these these composables will handle that for you as well as the whole hydration thing, and so you can get that, and you don't even have to know that this is happening. You maybe are using these already and don't realize that that's what's going on with the hood.

Alexander Lichter:

Yeah. Mostly just works, which is the great thing. Right? That's why I also use a framework. Just to add on there, it's it's obviously correct.

Alexander Lichter:

Like, useAsyncData and useFetch. They will they will save you from the cross state request pollution. They will save you from hydration errors because they fetch on the server. If you have your initial request, like, loading aside from, I don't know, coming from Google or, like, hot reloading, then that's happening. On the server side, data is passed over and then reused.

Alexander Lichter:

We also have a whole episode on the server side rendering part as well. Dollar fetch is very important.

Alexander Lichter:

There is a difference between the use things like use fetch, use async data, and dollar fetch. While dollar fetch is something you can use, I don't know, everywhere in your in your back end, like in Nitro, even in your express app, if you want to by just using OFetch or in your legacy Node. Js application, you don't need, a Vue or Nuxt context for that.

Alexander Lichter:

While the composables are, of course, something that only runs in there, which also means the composables, and there are also simple rules for that. Right? You should only use them in your setup function or in another composable. While the dollar fetch, of course, you can use in, let's say, a function that's a handler for an atclick event, perfectly fine. But it's, like, one of the most common mistakes I've seen, and that's also why, I made a video about this, like, how I call it you're using use fetch wrong, then, like, in parentheses.

Alexander Lichter:

I hope you don't. I think it's my most or second popular video so far on the channel. Because a lot of people, they are not aware of the the difference because if you call a composable accidentally like in, a click handler, it could lead to memory leaks. It could use, like because you use, refs usually with composables, it could lead to, like, accidentally sending a request on every button push. So that's all a bit tricky if you're not aware of that.

Alexander Lichter:

So definitely, worth checking it out and not misusing composables in general in your Vue application and also in the Nuxt application.

Michael Thiessen:

Yeah. The and the general role is sort of the the composables are are for setting things up on the when it when that component initially gets loaded, and then they're just supposed to react to things. And so if you have have that composable in the click handler, you're gonna reset up that data fetch every single time it gets clicked. And so then that's that causes issues.

Alexander Lichter:

Exactly. And it can't be cleaned up. That's the other thing. Right? Like, it's not not easy.

Alexander Lichter:

Like, usually, if you call them setup, then, oh, the component, is is being unmounted. It's being destroyed. Like, okay. Goodbye. But if it's in a in a click handler, it's like, yeah.

Alexander Lichter:

When will this ever end? It won't. It will be a a memory leak until, like, precise the functionality. Right? That's maybe accidentally doing weird things like submitting a submitting a a fetch request all the time when you just enter your password per letter.

Alexander Lichter:

Yeah. You you don't want all of that, but that's the way to solve it there. But, yeah, they they are fetching Nuxt is I would say it's very similar to how you do it in Vue as long as it's with form submissions and things. And, Michael, as you said, when you have, like, things that you wanna have on the server side as well, so also in your HTML, then you use the composables. You can even use them when you don't want that by just saying, like, server false, and then you just have these.

Alexander Lichter:

But usually they're there to select, okay, as you mentioned, they're reactive. So I set it up and say, I want to fetch data from that URL based on these parameters. And when they change, please refetch data with that new body to make sure I don't know. I click on a filter button. It adds, like, a query parameter in the URL, and then things will be refreshed because these change.

Alexander Lichter:

So that all works very, very seamlessly with use fetch and use async data.

Michael Thiessen:

One interesting thing I'd like to point out about the dollar sign fetch is that it's got this neat trick where it will know it knows if it's being called on the server. And so instead of if if you call a a route that's in your app, instead of doing the whole, you know, HTTP request to do a post to this endpoint, which then calls the event handler, well, it it's all part of the same Next app, so it can just call that event handler directly and just entirely bypass that whole, HTTP layer. And then, of course, when you're in the browser on the client, it knows that. And so then it, of course, makes that full request because, you know, the the server is not running locally in your browser. And so it's got this, like, neat little optimization, which, I think is pretty cool.

Alexander Lichter:

Yeah. That's thanks to Nitro, the the server engine where it just, as you said, says, oh, that's that's part of me anyway. I'll just run the actual event handler function

Michael Thiessen:

Yeah.

Alexander Lichter:

With the event that's already happening, and, and then we're good. Absolutely. That's it's very good in terms of performance, so you get bonus out of the box without doing anything, which is always great.

Michael Thiessen:

Yeah. So then we come to caching in Nuxt. And I believe that you have a whole video that you did on this. So maybe you, you wanna take this one.

Alexander Lichter:

Sure. Yeah. I have multiple, I think, because, yeah, there are there are, like, different caching layers from, like, caching on the I'll start with caching the client, caching on the server to have like these parts separate and caching on the client. Well, all the things that we talked about before in the client side, they apply as well. Right?

Alexander Lichter:

Like, make sure your server is returning the right HTTP headers to just say like, okay, we have the data already. It's all fine. That's all valid. But there is one more thing, especially with random data that you can do and make sure there is no actual call again with use fetch and use async data. And what you can do is use a low level function called get cached data.

Alexander Lichter:

And in there, you can basically say, oh, if there is data already and you can read it out, then return the data. And if you return, null or undefined right now, I think Nuxt 4 is only null, and undefined is is treated as a valid answer, then the data will be refreshed because we'll just say, like, okay. Fine. There is no data. I'll run it again.

Alexander Lichter:

Now that we talk about it, it's also one of some error cases where, oh, there is my API endpoint and returns null or undefined. And then use async data is rerun everywhere because, well, oh, the data is not there while it is, so don't don't do that. But, yeah, that's that's something that will also be improved because right now the code is very low level. You can even, like, set a TTL to say, like, okay, I want to have that in memory cache for, like, I don't know, 30 seconds or whatnot and then refetch it. That that all works.

Alexander Lichter:

But we plan on having some kind of composable utils for that. So you can just write, I don't know, cache colon and then ttl cache 1000 or something like that or, similar things. So the code is not duplicate. It's not that low level. You know, by reusing that composable utility, that's okay.

Alexander Lichter:

I use that type of caching. Lots of ways to plug in. But, yeah, that's, that's work in progress. There's an open issue for that. And let's see when it will happen.

Alexander Lichter:

But it's definitely neat if you want to, especially caching client side. So it works for SPAs, build with Nuxt file. All the server benefits, of course, are only there when you have an actual server. Otherwise, yeah, cache data is a is a good threat in all cases.

Michael Thiessen:

Yeah. And that's a that's a parameter that you pass as an option to the the fetch composable.

Alexander Lichter:

Yeah. To use fetch or use async metadata. Both both support that. Correct. Because also there as a note, use async data is more or less if you use use async data, just to have a little bit about the difference between use async data and use fetch, you can choose your fetching library.

Alexander Lichter:

So you can use axios there, you can use dollar fetch and whatnot. And usefetch is more or less a more elaborate version of use async data with dollar fetch together. So if you use them together anyway, then usefetch is doing a little bit more on top of that and, like, watching certain things. Also, there is a a big video about what's, the the difference there, but it's maybe just good to know if you wonder how should you use the one or the other? And the answer is depends if you use dollarfetch anyway, then just use usefetch and you're good.

Alexander Lichter:

If you wanna do a few more things, like, low level use async data is the way.

Michael Thiessen:

Yeah. Use async data gets any any async function. It just needs a promise.

Alexander Lichter:

Exactly. You can even just, like, do return promise that resolve if you want to. But, yeah, that's, of course, not not that helpful unless for testing. And yeah. And on the server side, we have we have a lot of things that we can do, thanks to Nitro, so thanks to the server engine.

Alexander Lichter:

So we have let let's start with route rules because they apply to the whole application. So Nuxt, you can basically say, okay, this path should be just pre rendered, right, or this path should be cached until the next deploy, or this path should be incrementally generated statically or this path should be cached, and then the cash should be renewed, without the user noticing. So SWR, style by revalidate behavior, and so on and so on. And you can do this for your Nuxt pages, but also for your API endpoints if you write them with Nitro or wrap them. There is one thing why I personally don't do that much with, the Nuxt pages themselves is because you can easily introduce, let's say, a different result between caching that initial request because it's only the server side runner page.

Alexander Lichter:

Right? So if you refresh the page, then the cache will be hit. But if you then navigate to another page and back, well, that API response has to come from somewhere as well. Right? So if you don't cache your API responses as well, and only a server on its side, you might get newer data when you navigate and then when you refresh to the old data.

Alexander Lichter:

So that's a bit tricky. So you either have to invalidate the cache nicely when the API response changes or cache API responses too, which I would recommend for sure.

Alexander Lichter:

And then if we come to your API endpoints in your application, so basically, whatever you write in in with Nitro and H3, you have a few more options. You can also use the route rules for that.

Alexander Lichter:

But then for caching, you can use define event handler, and you can use define cache event handler to pass in all the things you can pass in the route rules, but even a bit more like, what's the cache key name?

Alexander Lichter:

How should I cache that? Like, with which, storage should I cache? You can also do it in in the route rules. Say, like, okay. This page should be cached in my Redis or on Cloudflare KVKB or Vercel whatsoever, Upstash.

Alexander Lichter:

I don't know. Really up to you. MongoDB.

Michael Thiessen:

And that gives you more control than the route rules does. Right?

Alexander Lichter:

It it does give you to some degree, yes, for example, for the naming, mainly because in the Nuxt config, and also Nitro config because it also just works on plain Nitro, The config always has to be serializable, so you can't pass in a function or classes or whatnot. So if you have, like, a get key function or should invalidate cache or should bypass cache, you can't define that in in a config. There the the problem with that is that only works for, as I just said, like, your API routes, and not for Nuxt pages that you cache. We still have an open issue for that to have more fine grained control to say, like, okay. Hey.

Alexander Lichter:

Before you render all that, let's do something, like, make sure this is cached return a cached entry, whatnot, to make sure that's actually possible. But it's, yeah, it's also work in progress once again. But, yeah, with with these defined cached event handler, you can all do that, and then you have something called defined cached function. So if you say, I only wanna cache a part of my event handler or, like, a reusable utility, which is very common because sometimes you have like, oh, this user is authenticated. You don't want to cache the whole request, but maybe he's requesting some, let's say, GitHub repositories that he start.

Alexander Lichter:

Then you can just say, okay, I want to request the repositories as key. I use the user ID. So I have a cache base in that key and the path, or the user just requests whatever, a public resource, and you can just cache that then. And that's cached for all users. You can use define cached function, looks the same way as the event handler, but well, it's only for for a smaller part of that.

Alexander Lichter:

And once again, you have full control over that. And it's really, really useful, especially for that like fine grained caching when you still want to have all your requests actually coming in. So one common example is you wanna log user activity, then you can say, like, okay, I I wanna cache everything that's happening to request, but the request should still happen. And in the end, there's just, like, a log function called even after the request, was responded to. So even after response is sent, then, like, just log that little part, and then you're good.

Michael Thiessen:

So the the this caching is done through unstorage, I'm guessing?

Alexander Lichter:

Correct. So unstorage is used for that.

Michael Thiessen:

Yeah. So that's what gives you the this configurability of store it in in Redis, store it in memory, store it Correct. Wherever because you can have, you know, whatever kind of, storage thing you want with on storage. Yeah.

Alexander Lichter:

Yes. And that's that's pretty smooth. It it works very well, and, of course, you can use Unstorage also to store arbitrary data. I I recently had, like, a little application I built for one of my clients where I just stored some some plain information, in the file system with Unstorage, which is, super useful, especially for little demo back end. And even, like, whole videos, you can store with that.

Alexander Lichter:

So it's, it's really versatile.

Michael Thiessen:

Yeah. And on, one of our first episodes we did with Harlan, he where we talked about how he built this SaaS app in a week with Nuxt. He used Vercel's key value storage to to basically store all the analytics for for the for the application. So instead of, you know, hooking up a database and doing all that kind of stuff, it's just like 1 or 2 lines of code to just say set, and then you pass in the value and then, you know, a get call as well. It's yeah.

Michael Thiessen:

Yes. Really simple. It's really smooth. It's all you need.

Alexander Lichter:

Exactly. Also there, check it out episode number 2, I think it was, with Harlan because one was the need for SSR. And 3 was, I think, even Nitro. So, like, jump all the way back after the episode and and take a take a listen because it's super interesting. It was a a great journey, and experience there.

Alexander Lichter:

So, yeah, worth worth checking that out. Yep. And, yeah, in in the end, there there are quite some ways of caching, and people might wonder, how do I need, like, all of these layers because, well, there is a lot. The answer is you probably don't. Like, it depends really what you wanna do.

Alexander Lichter:

I would always suggest if you use SSR anyway, then, like, set up your API endpoints and cache them as aggressively as your data allows to do just because, well, why not saving up some time for that? Like, why don't the user wait if you already know the answer to the to the results? And with all the free tiers out there for kv stores or even just do it in memory, that only doesn't work if you have, like, I don't know, workers because then memory is just one instance, so that's tricky. So, like, yeah, just do that. And then I think on the client side, there's a lot of caching benefits to data.

Alexander Lichter:

Also, once again, where you know, okay, this takes long to to fetch, and maybe the API is not under your controls. You can't see the headers right. So it's also sometimes worth to to cache it there. But as usual, I would suggest to find, let's say, the hot parts of your application or, like, just the very slow parts and optimize these first. Because if it's like, I don't know, a 30 milliseconds request or, like, a 25 one, it doesn't really matter if it takes 5 seconds or, I don't know, 200.

Alexander Lichter:

It's a bigger difference. So Mhmm. Yeah, really check what slows your application down, then see how you can improve that and cache that. Maybe even for, like, a back end or front end, if you have, like, an API you you can't change, wrap it, take the data, transform it, store it, and build your own little back end for an API. And, yeah, that, usually works pretty well.

Michael Thiessen:

I have a question for you about use Nuxt data. So we've got this composable. It lets us access the data that you state and these fetching composables use fetch, use async data that they're using for these these caching that we we talked about this. How would you use use Nuxt data? Like, would you use it for caching at all?

Michael Thiessen:

Or, like, how does that fit into this this picture with caching and and all that?

Alexander Lichter:

Yeah. We haven't even talked much about using Nuxt data yet. As I said, yeah, you can access all the data that's already, let's say, there because it was fetched. You need a key for that. I probably wouldn't think about caching as the first use case.

Alexander Lichter:

I think more common is something like optimistic UI updates where you say, like, okay, I have the data and I do a call and I, like, I change something in the data already before having a return value just to have the the user like, let's just assume things go through. Right? We're good. Show the user a nice, I don't know, plus 1 on your vote count, and and then you're fine and then actually process the the real, response as soon as it comes in. So that's one thing also about having the user perceive the app as fast.

Alexander Lichter:

And if if it doesn't work, roll back instead of, oh, wait. Everything broke. Sorry. But I I don't think that's really for caching. Maybe for reusing data, that, of course, can be used too to say, like, okay.

Alexander Lichter:

I use it on a different spot. But then you could probably also use, a use fetch or use some data with immediate false, so it doesn't trigger straight away when you call it. But that depends a lot on on the use case. So it can be used to some degree for caching, especially a bit similar, like, get cached data in some cases, but I I think I never reached for that in in that context. Yeah.

Alexander Lichter:

So

Michael Thiessen:

Yeah. The use async data, like or yeah. The with the immediate false, and then you get the, is that an execute command, or was that API

Alexander Lichter:

recently changed? And refresh, and both do the same with just Okay. Just the same naming for, like, different context. Yeah.

Michael Thiessen:

Okay. Yeah. I think that's what confused me. Refresh, execute. Well, I guess the first time you should call execute, and then if you're if you've already called it, then switch to refresh.

Alexander Lichter:

I mean, it doesn't matter much. It's literally the same function. I was I'm even right now thinking about just creating an issue to select do we need both, or can we just rename it on the spot if you really need different context? But Yeah. Like, it's it's more of the semantics to say, like, okay.

Alexander Lichter:

I just call it once whenever the function is, like, ready, then I just call execute because it hasn't been executed before or do something like, oh, when I push that button, it actually refresh the data. But yeah.

Michael Thiessen:

Yeah. Yeah. I think that makes sense because, yeah, you if it's the first time you've called it, refresh is confusing.

Alexander Lichter:

But Yeah.

Michael Thiessen:

Yeah. So, you know, it's making things

Alexander Lichter:

Anything is hard if you don't take a look at the code and you don't know that these things are the same. Yeah. Maybe it's I think it should also be noted in in the JS doc somewhere, but who reads who reads the docs usually? Yeah. So it might be something to sneak in as an expert change.

Alexander Lichter:

But on the other hand, I mean, it's easy to refactor with a code. I'll think about it. I will also probably just ask the other team members what their stance on that is very soon. So maybe when an episode is out, there's already a 4 point x issue about that. It is

Michael Thiessen:

it is confusing to have the same thing named differently because you assume if there's 2 different things that they do different things.

Alexander Lichter:

Exactly. I mean, you can rename it depending on the on the situation. Right? So nothing stops you from, in that case, name and execute. In that case, just, like, destructure it and name it refresh.

Michael Thiessen:

Yeah. Yeah. There's probably a more generic term that you could use, like fetch or run or something that's like

Alexander Lichter:

Yeah. That's that's true. By who? Yeah. Also introducing these changes, I probably just would scrap one of these that's, like, less popular.

Alexander Lichter:

Because then if you introduce your 3rd one, it's like migration. It's just it's a bit of a pain. Yeah. But, yeah, it's it's definitely something maybe also to ask the community about how they feel about it if that was confusing to them. It's gonna be something that I will I will take to the team and and discuss.

Alexander Lichter:

But, yeah, in in the end, for for Nuxt, that's, that's more or less what what caching is there. Of course, there are, like, some more, in-depth, as I said before, like, videos on how to use these things, how to build stuff. And, yeah, I mentioned Nuxt scripts as well, which is also, like, not for your data fetching, but for the 3rd party scripts there and super useful image, of course, as well, Nuxt image, fonts, like, all the core packages, so to say, really worth checking these out.

Michael Thiessen:

It's interesting that, like, there's a lot more to talk about when it comes to Nuxt and data fetching and caching because Nuxt gives you more tools to work with. And that's, like, the point of these meta frameworks is that, yeah, if you gives us a bunch of stuff to to work with, in terms of data fetching, it doesn't do anything really, which is the way it should be. But then next comes along and adds on all these extra things to make our lives better. And, clearly, both of us are on the the Nuxt train, and, we aren't getting off anytime soon. So

Alexander Lichter:

That's that's true. That's true. I also think the other the other part is that Nuxt adds a little bit of complexity as well, as in, like, server side rendering is is great and some more complexity. So if you don't need that, just turn it off. It's also totally fine.

Alexander Lichter:

Or, like

Michael Thiessen:

Yeah.

Alexander Lichter:

You can even turn off of a route rule. So, like, hey, my app and dashboard doesn't need that, which is totally true. But performance wise, SR can be really beneficial because then you can actually do these things like, oh, cache stuff, get all the API calls together and then then send out data and cache them. So instead of in an SPA, you have no choice, other than, hey. I call these APIs.

Alexander Lichter:

I can transform things on the client, but I need to load all the data. So, that's always a bit of an issue. Then you might start, like, okay. I can build my own API to do that. So you have a different API service and an SPA.

Alexander Lichter:

And in a way, that's also fine. But then, still, you have to do that API call, and then it starts with performance once again with, like, okay. The user sees a long loading spinner, and that's not ideal, and so on and so on. So I think SSR has its merits, but surely not every application needs it.

Michael Thiessen:

Yeah. Yeah. I think that's that's a really good point that, there there are trade offs. Right?

Alexander Lichter:

And so 100%.

Michael Thiessen:

You introduce complexity that you need extra stuff to handle that complexity.

Alexander Lichter:

But I'm I'm very happy that, yeah, with with Nars, you can just start building a plain Vue application without technically any feature that you want. Right? Like, you can you can say, okay. I use my own router, options file. I set up my own routes.

Alexander Lichter:

I ignore everything around it. So, like, migrating a Vue application, what, finally, Daniel did on the stream recently, also linked to that, somewhere there, there is show notes. And I for everyone listening, I point down on on your phone's description, you know. And, so so that's, that's definitely something to take a look at because people are also like, oh, yeah. All this complexity, all these features, is it really worth moving over?

Alexander Lichter:

And it's difficult. And depending on your application and what you wanna use, but, like, moving over and having still an SPA is not that tricky. Then, of course, enabling SSR, changing things, it might be a bit more, but even that is is possible.

Michael Thiessen:

Yeah. And one one last thing I'd like to add is that sometimes it can feel like Nuxt has so much stuff, and you think, oh, I only wanna use a few features. Why would I use this big heavy framework that's gonna include all this stuff that I don't actually need? But Nuxt actually just only adds in the stuff you need. And so if you don't need a router, then it won't include the router.

Michael Thiessen:

And if you don't need, you know, some sort of functionality, it's not gonna include that in there for you. And, like, tree shaking things and, you know, getting that bundle size really small, that's one of the the things that Nuxt does really well. And so you can just, like, add in features as you need them, and Nuxt will just, like, happily put that in there for you, and you don't have to worry as much about, bundle size.

Alexander Lichter:

100%. I don't know. If you come back to data fetching and that, if you'd like if you have a plain SPA, you could just use all of fetch in your own composable for that. You don't need to migrate to use fetch and use async data because, okay, all fine. You it's good.

Alexander Lichter:

But if you then consider using SSR at some point, of course, it's helpful to have that. And they also provide some DX ergonomies that are really nice. So it's worth checking it out. But if you said, okay, I have a code base that's working, then you're good. Right?

Alexander Lichter:

That's, once again, the beauty of choice to say, like, okay. These features, I don't need them or don't want them now. I just don't have time to implement them. Same for, like, migration stuff. So, yeah, that's great.

Michael Thiessen:

I think that, brings us to the end of this episode then.

Alexander Lichter:

Absolutely. We could probably also touch on, like, REST APIs and GraphQL and everything, but I think that's also a bit of beyond the the Vue scope. Maybe GraphQL with you could be very interesting for a future episode. But, yeah, that's, once again, could be another podcast episode besides the WASM one we we talked about.

Michael Thiessen:

I have a lot of, flashbacks to, GraphQL and Vue. So

Alexander Lichter:

Good or bad flashbacks?

Michael Thiessen:

Yeah. Bad ones. So maybe I'd skip that episode. Or maybe I I should be there. It'll be a good

Alexander Lichter:

Like, have a healthy discussion. Get out yeah.

Michael Thiessen:

It'd be, you know, therapeutic for me to, talk it out.

Alexander Lichter:

Yeah. Let's, let's make it happen. Get someone on who's using GraphQL locally because I I also don't really but I know quite a few people who might do in YouTube.

Alexander Lichter:

So, if also, once again, if you are interested out there, write us. Let us know.

Alexander Lichter:

And, of course, check out the other episodes. We mentioned so many. Link the link on the show notes. So just start from 1 and listen for all of them again. Check out, well, Michael's website, and stay tuned for next episode every Thursday afternoon in in European time and 1st day slash Friday morning everywhere else.

Alexander Lichter:

So, yeah, go for it.

Michael Thiessen:

Alright. Well, see you in the next episode.