Rate limiting in nextjs using Redis
Created on: Aug 13, 2024
Let's understand the concept by creating a nextjs app.
npx create-next-app@latest ratelimiting cd ratelimiting
After this create directory pages/api
to write a sample rest api. under this folder create a file called hello.ts
with below code
import type { NextApiRequest, NextApiResponse } from "next"; type ResponseData = { message: string; }; export default function handler( req: NextApiRequest, res: NextApiResponse<ResponseData> ) { if (req.method === "GET") { res.status(200).json({ message: "Hello world" }); } }
Now let's run out app and hit api multiple times say 50 times.
seq 1 50 | xargs -n1 -P50 -I{} curl -s -o /dev/null -w "Request {}: %{http_code}\n" http://localhost:3000/api/hello
If such request comes in number like more number, it can prevent legitimate request from some genuine client. One of the approach to prevent this is by using rate limiting.
Rate limiting is a technique used to control the number of requests a user or system can make to a server within a specific time period. It helps prevent abuse, protect resources, and ensure fair usage by restricting excessive or malicious traffic.
In nextjs we will use Vercel Edge Middleware middleware of nextjs to apply rate limiting.
Before going ahead we have to create a account on upstash. It is pretty simple. Then under Redis tab, create a redis database.
Then click on eye option to revel secrets. copy url and token and add in .env file.(create a .env file first)
VERSEL_REDIS_URL=<url> VERSEL_REDIS_TOKEN=<token>
Now let's write a middleware which will allow five connection in 20 seconds for a ip address.
import { ipAddress, next } from "@vercel/edge"; import { Ratelimit } from "@upstash/ratelimit"; import { Redis } from "@upstash/redis"; const redis = new Redis({ url: process.env.VERSEL_REDIS_URL, token: process.env.VERSEL_REDIS_TOKEN, }); const ratelimit = new Ratelimit({ redis, limiter: Ratelimit.slidingWindow(5, "20 s"), }); export const config = { matcher: "/api/hello", }; export default async function middleware(request: Request) { const ip = ipAddress(request) || "127.0.0.1"; console.log("middleware is called"); const { success, pending, limit, reset, remaining } = await ratelimit.limit( ip ); return success ? next() : Response.redirect(new URL("/blocked.html", request.url)); }
Let's call api ten times and check. we will get 200 for only 5 request. for other we will get 302.
You can check whole code in github