How to save data with Next.js Middleware & Edge functions (using serverless Redis)

In this article we will learn how to save and fetch data from Redis inside of a Next.js middleware & edge functions. We will do this by building a request logger middleware for a fresh Next.js application. You can add this middleware also to your existing application. The middleware will save request data to Redis for each request made to the api routes. We will use Upstash Redis for handling setting up and maintaining the Redis database. Let's get started!

If you rather watch a video, I also made a video about this on my Youtube channel! Watch on Youtube

Create the middleware

First initialise a new Next.js project.

1yarn create next-app nextjs-request-logger

Navigate to the /pages/api folder and create a new file called _middleware.js.

1touch pages/api/_middleware.js

It is important that you name the file exactly as described above because this is how Next.js knows that this file will act as a middleware. Code inside middleware will be run on each request that is made to any route inside this folder (/api) or any of its subfolders / subroutes.

Next add the following code to the _middleware.js

1import { NextResponse } from "next/server";
2
3export default async function middleware(request) {
4  const response = NextResponse.next();
5
6  const time = Date.now();
7  const timeStr = new Date(time).toLocaleDateString();
8
9  const logData = {
10    time: timeStr,
11    url: request.url,
12    ip: request.ip,
13    ua: request.ua,
14    geo: request.geo,
15  };
16
17  console.log(logData);
18
19  return response;
20}

This will get information from the request and format it to the logData object and for now, just log it to the console. To test it, start your server with yarn dev, open localhost:3000/api/hello in the browser and check out your terminal, you should see the request information logged to the terminal.

Terminal showing the logged request info

Now our middleware is getting information from the request and storing it to the logData object. Only thing left to do is to save that logData object to Redis!

Save data to Redis

We will use Upstash Redis for handling the Redis database. Upstash offers a REST api for interacting with the Redis database. This is great because we need to interact with the Redis database inside our middleware. Middleware is run as edge function once we deploy it and having to use some Redis client connection inside the edge function would be prone to bugs compared to just making a REST api request once we want to save data to the database.

First create a new folder called lib to the root of your project and redis.js file inside that folder.

Next navigate to console.upstash.com and sign up or log in to your Upstash account. Once you are logged in, go to the Redis tab and create a new database.

Upstash dashboard

Create new Redis database

Once you have created the database, go to the Details tab of your database and you should see "REST API" section.

Redis REST api

In the section click "@upstash/redis" tab and you should see a Javascript code snippet.

Redis REST api connection information

This code snippet uses @upstash/redis npm package to initialise a Redis REST client that interacts with the REST api. Copy the code and paste it in to your lib/redis.js file and add an export statement to the end of the file. Your lib/redis.js file should look like this (excluding the values of url and token).

1import { Redis } from "@upstash/redis";
2
3const redis = new Redis({
4  url: "https://eu2-talented-chimp-30122.upstash.io",
5  token:
6    "UJKJACQgMjJlMmRmMDItYzNjNi11N2Y1LTgxMWQtNmNlZjkyMjg3NmJjNjRhNWY5N2JjYTUwNDQ4NDk1YmI3NDAyOGZhYzQwMzY=",
7});
8
9export { redis };

Next import the Redis REST client we just made, to the _middleware.js file.

1// ...
2import { redis } from "../../lib/redis";
3// ...

To save our logData to Redis using the Redis REST client, add the following code after the logData definition.

1// ...
2redis.lpush("api-request-log", logData);
3// ...

This will push the logData object to a list called "api-request-log" using the REST api of Upstash Redis. Your _middleware.js should look the following.

1import { NextResponse } from "next/server";
2import { redis } from "../../lib/redis";
3
4export default async function middleware(request) {
5  const response = NextResponse.next();
6
7  const time = Date.now();
8  const timeStr = new Date(time).toLocaleDateString();
9
10  const logData = {
11    time: timeStr,
12    url: request.url,
13    ip: request.ip,
14    ua: request.ua,
15    geo: request.geo,
16  };
17
18  redis.lpush("api-request-log", logData);
19
20  return response;
21}

Now we have a middleware that saves data to Upstash Redis.

Fetch data from Redis

We are now saving every request made to /api* to Redis database but how can we get the saved data from the database and display it?

Let's first create a new page that will be used for displaying the json data from the Redis database. Create a new page pages/view-log.js and add the following code to it.

1import { redis } from "../lib/redis";
2
3export default function ViewLog(props) {
4  return (
5    <div>
6      <h1>Api request log</h1>
7      {props.entries.map((entry) => {
8        return (
9          <div>
10            <pre>{JSON.stringify(entry, null, 2)}</pre>
11          </div>
12        );
13      })}
14    </div>
15  );
16}
17
18export async function getServerSideProps(context) {
19  const entries = await redis.lrange("api-request-log", 0, -1);
20  return {
21    props: { entries },
22  };
23}

On the first line we import the Redis REST client we made in the previous step. Earlier we used it to save data through the REST api but it can also be used for fetching data from the database.

Then we have the ViewLog component which will loop through props.entries and display them. The props.entries are set in the getServerSideProps function.

In the getServerSideProps function we use redis.lrange("api-request-log", 0, -1) to load the data from the Upstash Redis database. First parameter is the list name we want to fetch, second parameter is the starting position and third parameter is the end position. By using -1 for the end position we tell the function that we want to fetch all of the items in the list.

Now when you navigate to localhost:3000/view-log you should see all the log requests that are saved to Upstash Redis.

Conclusion

Building this kind of request logger middleware with Next.js middleware doesn't actually require that much effort. When we place the logger code to the api/_middleware.js we automatically have logging for all api route requests thanks to how Next.js middleware works.

Using Redis inside Next.js middleware and edge functions was also easier than I first thought. Thanks to Upstash we were able to easily spin up a Redis database and connect to it via REST api. Saving and loading data to & from the database was easy and straightforward.

You can find the repo for this article here: https://github.com/tumetus/nextjs-request-logger.

If you enjoyed this article be sure to subscribe to my newsletter and Youtube channel!


Read next:
If you want to learn more, make sure to subscribe on Youtube!