TYPeee

Implementing Rate Limiting in Next.js 13/14 with Upstash

Types of Layers and Rate Limiting

There are Layers we need to consider and Rate Limiting can be applied to these Layers with different purpose and method.

 

1. Network Layer

Part of the OSI(Open Systems Interconnection) model or TCP/IP(Transmission Control Protocol/Internet Protocol) model. It deals with the routing and forwarding of data packets between devices across different networks. The protocols operating at this layer include IP(Internet Protocol), ICMP(Internet Control Message Protocol), ARP(Address Resolution Protocol) and more

  • Rate Limiting can be enforced at the Network Layer, such as firewalls or load balancers, to filter incoming requests before they reach the application servers.

 

2. API Layer

 The API layer refers to the part for handling incoming requests and producing responses to external systems or client. It acts as an interface between the front-end user interface or client applications and the back-end database or services. These layer often consists of endpoints that follow RESTful principles and handle HTTP methods(e.g. GET, POST, PUT, DELETE, etc...). 

  • Rate limiting in the API Layer protect their APIs from abuse, prevent overloading of back-end system, and ensure fair access for all client.

 

3. Application Layer

The highest layer in the OSI model or TCP/IP model. It provides interfaces for end-user applications to access network services. Protocols operating at this layer include HTTP(Hypertext Transfer Protocol), FTP(File Transfer Protocol), SMTP(Simple Mail Transfer Protocol), and DNS(Domain Name System)

  • Examples of application layer are Web Browser, email client, and file transfer programs
  • Rate limiting can also be implemented within the application logic itself. This allows for more fine-grained control over specific endpoints or operations. For example, you can limit the number of requests a user can make within a certain time period or restrict access to resource-intensive endpoints.

 

4. Database Layer

Database Layer is conceptual layer in software architecture. It managing and storing data in a structured format for efficient retrieval and manipulation. This layer interacts with the application layer through database queries and transactions, providing data services to application

  • Rate limiting can be applied to database queries to prevent excessive load on the database servers. This helps to ensure optimal performance and prevent denial of service attacks targeting the database.

 

5. Which layer should i set rate limiting?

Typically, Rate Limiting implemented at the API layer to control the rate of incoming requests from client. This ensures that your server infrastructure is not overwhelmed by a large number of requests from a single client or a distributed set of clients.

 

Decide the Route to Set Rate Limiting

Before implementing rate limiting, we needs to decide which route will be applied rate limiting. Rate limiting is more relevant at the routes where requests are processing fetching data from databases, manipulate data, authenticate user, integrating with external services or perform other server-side task. In Other words, the route more intensive operations and more likely to be targeted by malicious actors or experience heavy traffic is more relevant.

How about the route that return Page.js?

Page routes in Next.js primarily handle client side rendering and navigation, serving HTML, CSS, and JavaScript files to user's browsers. These routes are generally less resource intensive and are not typically the focus of rate limiting measures.

However, if your application's page routes are making requests to APIs or performing server side operations, it's essential to consider rate limiting at the API layer to protect against abuse or overload

 

1. Rate Limiting at Each Route

1) API route(e.g. /api/...)

API routes are responsible for fetching data from the database, performing intensive processing, or handling other server-side tasks. Therefore, rate liimiting will be applied to these routes

 

2) Page route(e.g. /, /post/[id], /topic/[topic], etc...)

Page routes are a bit more ambiguous. When considering rate limiting for page routes, factors such as the type of rendering, whether intense processing is performed, and whether APIs are used should be take into account

 

3) Static asset(e.g. /_next/static, /_next/image, /favicon.ico, etc...)

Rate limiting will not applied to these routes because these routes only serve static assets.

 

2. Rate Limiting at Different Level

  • Global Rate Limiting: This applies the same rate limit to all incoming requests regardless of their source. It ensures that the overall traffic to your API stay within predefined limits
  • Per-Client Rate Limiting: This sets different rate limits for individual client or API consumers based on their identities, API keys or IP addresses. It allow you to control the usage of your API on a per-client basis, preventing any single client from consuming excessive resources.
  • Per-Route Rate Limiting: This applies rate limits to specific API routes or endpoints. It allows you to enforce stricter limits on certain endpoints that are more resource intensive or critical to your application's functionality.

Implementing Rate Limiting with Upstash

1. Create a Redis database using Upstash Console. Check Document

typeee image

 

2. Get UPSTASH_REDIS_REST_URL, UPSTASH_REDS_REST_TOKEN and store it in .env.local file of your Next.js project.

typeee image

 

3. Install @upstash/ratelimit and @upstash/redis in your project

text
1npm install @upstash/ratelimit @upstash/redis

 

4.Implement Rate Limit in Next.js 14 middleware.js

src/middleware.js
1import { NextResponse, NextRequest } from "next/server";
2import rateLimit from "@/lib/ratelimit";
3
4
5export async function middleware(request) {
6  const origin = request.nextUrl.origin ?? "";
7  const path = request.nextUrl.pathname ?? "";
8  const ip = request.ip ?? request.headers.get("X-Forwarded-For") ?? "unknown";
9  const response = NextResponse.next();
10  let limitResult;
11
12  //set rate limit to each routes
13  if (path.startsWith("/api")) {
14    limitResult = await rateLimit("api_" + ip);
15  } else {
16    limitResult = await rateLimit("page_" + ip);
17  }
18
19  //redirect when exceed
20  if (!limitResult.success) {
21      if (path === "/ratelimit") {
22        return new Response.sendStatus(429);
23      } else {
24        return NextResponse.redirect(new URL("/rate-limit", origin));
25      }
26    }
27  
28  //set response headers related to rate limit
29  response.headers.set("X-RateLimit-Limit", limitResult.result);
30  response.headers.set("X-RateLimit-Remaining", limitResult.remaining);
31
32  return response;
33}
34
35export const config = {
36  matcher: [
37    {
38      source: "/((?!api/auth|_next/static|_next/image|favicon.ico).*)",
39      missing: [
40        { type: "header", key: "next-router-prefetch" },
41        { type: "header", key: "purpose", value: "prefetch" },
42      ],
43    },
44  ],
45};
46
47
/lib/rate_limit/rate_limit.js
1import { Redis } from "@upstash/redis";
2import { Ratelimit } from "@upstash/ratelimit";
3
4const rateLimiter = new Ratelimit({
5  limiter: Ratelimit.slidingWindow(7, "10 s"),
6  redis: Redis.fromEnv(),
7  analytics: true,
8  ephemeralCache: undefined,
9  prefix: "@upstash/ratelimit",
10  timeout: undefined,
11});
12
13export default async function rateLimit(identifier) {
14  try {
15    const result = await rateLimiter.limit(identifier);
16    return result;
17  } catch (err) {
18    console.error(err.message);
19  }
20}

 

Related Posts