SecurityRate limiting (API Routes)

It is recommended to protect your API routes from abuse by rate limiting them. This tutorial will show you how to rate limit your API routes using Upstash.

We will be protecting API routes /api/one and /api/two. You can replace them with your own API endpoints.

Setup

  • Sign up on Upstash
  • Create a new Redis database
  • Add the UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN to your .env.local file
  • Install the packages:

    terminal

    1npm install @upstash/redis @upstash/ratelimit
  • Create a new middleware.js file in the root directory (the same level as the /app folder) and add the following content:

    /middleware.js

    1import { NextResponse } from "next/server";
    2import { Ratelimit } from "@upstash/ratelimit";
    3import { Redis } from "@upstash/redis";
    4
    5const redis = new Redis({
    6  url: process.env.UPSTASH_REDIS_REST_URL,
    7  token: process.env.UPSTASH_REDIS_REST_TOKEN,
    8});
    9
    10const ratelimit = new Ratelimit({
    11  redis: redis,
    12  limiter: Ratelimit.slidingWindow(5, "60 s"),
    13});
    14
    15export default async function middleware(request) {
    16  const ip = request.ip ?? "127.0.0.1";
    17  const { success } = await ratelimit.limit(ip);
    18
    19  return success
    20    ? NextResponse.next()
    21    : NextResponse.redirect(new URL("/blocked", request.url));
    22}
    23
    24export const config = {
    25  matcher: ["/api/one", "/api/two"],
    26};
    27
    We are rate limiting the user to 5 requests per minute based on their IP using the sliding window algorithm. You can refer to the Upstash ratelimit SDK documentation for more information on customizing it.
  • Create a new /app/blocked/page.js file. This is the page the user will be redirected to when they hit the rate limit. Add the following content:

    /app/blocked/page.js

    1"use client";
    2
    3import config from "@/config";
    4import { signIn } from "next-auth/react";
    5import React from "react";
    6import Link from "next/link";
    7
    8const Blocked = () => {
    9return (
    10  <main className="relative bg-neutral text-neutral-content h-screen w-full flex flex-col justify-center gap-8 items-center p-10">
    11    <h1 className="text-xl md:text-2xl font-medium">
    12      Hm, Access Blocked
    13    </h1>
    14    <p>Try again in 1 minute</p>
    15
    16    <div>
    17      <button
    18        onClick={() =>
    19          signIn(undefined, {
    20            callbackUrl: config.auth.callbackUrl,
    21          })
    22        }
    23        className="link"
    24      >
    25        Login
    26      </button>{" "}
    27      or{" "}
    28      <Link className="link" href="/">
    29        Home
    30      </Link>
    31    </div>
    32  </main>
    33);
    34};
    35
    36export default Blocked;
  • That's it! You have successfully rate limited the API routes. Now, when a user hits the rate limit, they will be redirected to the /blocked page.