Lesson Description
The "Redis Page View Counter" Lesson is part of the full, Build a Fullstack Next.js App, v4 course featured in this preview video. Here's what you'd learn in this lesson:
Brian demonstrates how to implement a page view counter using Redis. He shows how to create a server action that increments views for each article, integrates it with the client using a `useEffect` hook, and displays the counts in the UI. He also discusses caching behavior, eviction strategies, and why Redis is useful for scalable, high-traffic applications.
Transcript from the "Redis Page View Counter" Lesson
[00:00:00]
>> Brian Holt: So, I think that's, oh yeah, we got one more thing that I want to do with Redis here. We are going to go make page views, because we want to be able to see on all the wiki pages how many people have viewed them. So let's go to actions here and we're going to make a new action. And we're going to call it page views.ts.
[00:00:30]
I would say use server, because it's a server action, import Redis from cache. Const key4 equals. Yeah, I mean, I was being a bit more defensive with my types here, but I think it's almost always a string. But in any case you. Get returns back page views. Article ID and then export. Async function.
[00:01:25]
And this is going to be called increment page view. And it takes in an article ID, which is a number. OK, and then const article key goes key4 article ID. Const newVal equals await Redis increment. Article key and return plus newVal, because we want this to be a number. So, we don't even need this, this can just be a number.
[00:02:18]
We don't need the or string, because this could never be a string. So what does this do? This takes in an article ID. It's going to then store that in Redis. If it doesn't exist, it'll create a new one with 1 being the number. Well, that's what increment does. If it does exist, it'll increment it to be 2, 3, 4, 5, 6, 7, 8, 9, 10.
[00:02:41]
And the nice thing is that it gives you back the new number as well. OK, let's go put this into our wiki article viewer, wiki article viewer. So at the top here, please import increment page view from app actions page views like that. And we're going to have everyone's favorite, a useEffect. Honestly, thinking back on this, I'd probably make this a custom hook, because that would be a really nice place for it to be.
[00:03:18]
I didn't do it that way. I just did it with an effect. But this is a client, right? This is all happening on the client for a variety of reasons, so it cannot be like a server thing. OK, useEffect async function fetch page view. Page view. And then here we're going to say const newCount equals await increment page view with article ID.
[00:04:07]
And set local, and we need to go create this hook as well. Local page view with newCount or 0, one of the two. Either is fine. Let's go make a hook up here for const local page view, local page views, set local page views equals useState, so 0. OK, under the article badge, which I have labeled down here article badge.
[00:05:21]
We're going to say div, className equals ml-3 flex items-center text-small text-muted-foreground. And we're going to put a span. It's not a spa, though I could use a spa right now. Span, oh, I forgot to put there as well. We need the Eye, which we're going to pull in from Lucide React as well. So if I click that up here, notice it'll pull in Eye from right here, from Lucide React.
[00:06:18]
So we'll put the Eye there. ClassName equals h-4 w-4 mr-1. And span. And here we're going to say either local page views or, because I don't want to have some ugly null or like some empty space there. I toyed around with a bunch of stuff and I found out like the just like an em dash works really well here.
[00:07:01]
And span. I don't need two, I just need the one. ClassName equals ml-1. Views. OK. And I need a closing curly there. So I wrote this, this is what we just put in there. OK. Let's go see if it works. Read article. And this has no views at the moment, right? And it still doesn't have any. Let's go figure out why.
[00:07:45]
Oh, did I actually like finish that? I know exactly what I did. UseEffect, it's a very lovely function that is doing positively nothing at the moment. You actually have to call it. So why am I defining this in the effect, right? I actually wanted to run inside of the effect, but I still want to be able to use the variables from the closure.
[00:08:03]
So that's why we've done it this way. There's a bunch of ways to do this, but I was, that's why I was saying that a custom hook would be like an elegant way to do this. But now, nice. Look, we're going off the charts here. What is happening here? Maybe stop. Oh, you know what, it's because increment page views, set local page views.
[00:08:54]
I think I need to put in a. Yep, that's exactly what it is right here. God, I mean, useEffect, man. Yeah, and whatever. It needs to say that this depends on article.id, which won't change, but still. Still, it was fun to go viral. 577. Don't mind me, I just called Redis 600 times, casually. But notice that it's going up every single time, right?
[00:09:38]
Which is kind of cool. But if I go to a different article. 234. I think it's actually overestimating by one, but who cares? It's their page views, right? And now if we go over to our data browser here, we can see each one of these is starting to carry a couple different articles in there, right?
[00:10:03]
So, another perfectly great use case for Redis. Again, if we evicted this entire cache, no one's really going to care, right? And you could also write a query like, hey, every night, go and grab my page views so that if I did crash this or if something went wrong with Redis, then you could like save it to S3 or something like that.
[00:10:29]
Or to the database, right? You can actually update the database directly with the pages. The TTL in here, TTL stands for time to live, right? Whereas like you can see this one, like the, well, that's gone now, but the TTL on that would have said 50 seconds, right? It's basically like, when does this get evicted from the cache?
[00:10:48]
The other thing to know about Redis is like, usually there's like a logical limit of how big the cache can go, at which point it'll start evicting things to keep it a certain size, so you'll say like, I want this cache to be 2 gigabytes. And when you get out, when you get to 2 gigabytes, start dumping other stuff from the cache when you set new stuff to it.
[00:11:11]
So there's a couple of strategies, the most common one's like first in, first out, right? So, or like, usually it's a least recently updated, right? So if something's been in the cache for 2 years and not read from, that's going to be the first thing that's going to get evicted from your cache, which is another thing that I keep saying like this data is meant to be not highly valuable.
[00:11:33]
If Redis is the source of your truth and like that's all you have, you're going to have a bad time. I did hear of one startup that did everything based on Redis. They had no other database other than Redis, and they just like built in some amount of tolerance for data loss into their product, and I was like, why would you do that?
[00:11:53]
Don't get me wrong, it's extremely fast and it's very cheap to run. But machine time is usually much cheaper than human time and debugging those issues sounds like horror, right? So, don't, this is my plea to you. Unless you do it, then tell me how it goes. OK. Questions? Yeah, how does Redis compare to Next.js's built-in data cache?
[00:12:26]
I mean, different use cases, right? This is meant to be like a remote cache, so the nice thing about like web servers is they're generally somewhat stateless, right? So if I have one Next server and then all of a sudden I go viral, then I can scale up to 3 Next servers, 5 Next servers, 15 Next servers, and they don't have to coordinate with each other because they're all stateless.
[00:12:47]
But they still have to read from the same database, right? And the same database will give the same data to all of them, the same cache will give the same data to all of them. Now with Next cache, there's abilities to like write that to like Redis essentially is where you would write it to, but if you're keeping everything in memory on the same server, and all of a sudden you have two servers, they're going to have different caches, right, which may be fine, may not be fine.
Learn Straight from the Experts Who Shape the Modern Web
- 250+In-depth Courses
- Industry Leading Experts
- 24Learning Paths
- Live Interactive Workshops