Add rate limits to NextJS APIs
Tags: javascript, nextjs
Published: 2024-04-09 03:57:08

Implementing Rate Limiting in Next.js App Router with In-Memory Counters 🔒⏱️

> Demo :- https://ratex-nu.vercel.app/api
> Codes :- https://github.com/SH20RAJ/rateLimitjs



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b87m1o8ds28ucxmlg1h6.gif)




Introduction:

Rate limiting is like a friendly bouncer at a party 🕺, making sure everyone gets a fair share of the fun without overwhelming the venue. 🏘️ It's crucial for protecting your web applications from excessive load and potential abuse, preventing Denial of Service (DoS) attacks, and ensuring the overall stability and performance of your system. 🛡️ With the introduction of the new app router in Next.js 13, implementing rate limiting may seem a bit different from the traditional approaches used in the pages router. But fear not, my friends! 💪 In this article, we'll explore how to set up rate limiting for your Next.js app router API routes using in-memory counters. 📈

Setting Up Rate Limiting with In-Memory Counters:

First things first, let's create a utility function that will handle the rate limiting logic. Create a new file called `rateLimit.js` in your project's `utils` folder (or any other location you prefer): 📂

```javascript
let requestCounter = {};
let resetTimer;
const time = 5 * 1000; // 5 seconds
const requestLimit = 4; // 4 requests per 5 seconds

// Function to reset request counts
const resetCounters = () => {
  requestCounter = {};
  clearTimeout(resetTimer);
  resetTimer = setTimeout(resetCounters, time);
};
resetCounters(); // Start the timer

export default async function rateLimit(req, res) {
  const ip = (req.headers.get('x-forwarded-for') ?? '127.0.0.1').split(',')[0];
  const clientIp = ({ ip }).ip;

  requestCounter[clientIp] = requestCounter[clientIp] || 0;

  if (requestCounter[clientIp] >= requestLimit) {
    return true; // Rate limit exceeded 🚫
  } else {
    console.log('Request count for IP:', clientIp, '=', ++requestCounter[clientIp]);
    return null; // Proceed with the request ✅
  }
}
```

In this code:

1. We initialize an empty object `requestCounter` to store the request counts for each client IP address. 📋
2. We define the `time` variable as `5 * 1000` milliseconds (5 seconds) and the `requestLimit` variable as `4`, allowing a maximum of 4 requests per 5 seconds. ⏱️
3. The `resetCounters` function resets the `requestCounter` object and sets a timeout to call itself after the specified `time` (5 seconds in this case). 🔄
4. The `rateLimit` function is an async function that takes the `req` and `res` objects as parameters. 🔍
5. Inside the `rateLimit` function, we first retrieve the client's IP address from the `x-forwarded-for` header or use `127.0.0.1` as a fallback. We then extract the first IP address from the comma-separated list (in case there are multiple IP addresses in the header). 🌐
6. We initialize the request count for the client IP address to 0 if it doesn't exist in the `requestCounter` object. 🆕
7. If the request count for the client IP address is greater than or equal to the `requestLimit` (4 in this case), the function returns `true`, indicating that the rate limit has been exceeded. 🚫
8. If the request count is below the limit, it increments the request count for the client IP address, logs the updated count, and returns `null`, indicating that the request should proceed. ✅

Applying the Rate Limiting Middleware:

Now that we have the rate limiting middleware set up, we can apply it to our Next.js app router API routes. Open the `route.js` file for your API route and import the `rateLimit` function: 📝

```javascript
import rateLimit from './rateLimit';

export const GET = async (req, res) => {
  if (await rateLimit(req, res)) {
    return new Response(JSON.stringify({ error: 'Rate limit exceeded. Please try again later.' }), {
      status: 429,
      headers: {
        'Content-Type': 'application/json',
      },
    });
  }

  return new Response(JSON.stringify({ message: 'Hi' }), {
    status: 200,
    headers: {
      'Content-Type': 'application/json',
    },
  });
};
```

1. This file imports the `rateLimit` function from the `rateLimit.js` file. 📥
2. Inside the `GET` route handler function, it calls the `rateLimit` function with the `req` and `res` objects and awaits the result. ⏳
3. If the `rateLimit` function returns `true` (meaning the rate limit was exceeded), it returns a `429 Too Many Requests` response with an error message. 🚫
4. If the rate limit was not exceeded, it returns a `200 OK` response with the message `{ message: 'Hi' }`. 👋

With this implementation, your Next.js app router API route will be protected by the rate limiting middleware, allowing a maximum of 4 requests per 5 seconds for each client IP address. 🔒

Conclusion:

Rate limiting is an essential technique for securing and optimizing your web applications, just like a friendly bouncer making sure everyone has a good time. 🕺 By following the steps outlined in this article, you can easily implement rate limiting for your Next.js app router API routes using in-memory counters. 📈 This approach is suitable for development and small-scale applications, but for production environments, it's recommended to use a more robust solution, such as a database or a dedicated rate limiting service, to ensure better scalability, persistence, and reliability. 💪

Remember, this implementation uses an in-memory counter, which means that the rate limiting will be reset when the server restarts. 🔄 Additionally, it's essential to handle the `x-forwarded-for` header with caution, as it can be spoofed by malicious clients. 🕵️‍♂️ In a production environment, it's recommended to only trust the `x-forwarded-for` header if you're running behind a trusted proxy server that you control. 🔐

Now go forth and implement rate limiting in your Next.js app router like a champ! 🏆 And remember, a little bit of rate limiting goes a long way in keeping your applications safe and sound. 🛡️


---

> This technique has a catch, can you identify in comments and also has a solution. Let's increate engagement in comments. 😇😅

> [GitHub](https://github.com/SH20RAJ?tab=repositories) || [Twitter](https://x.com/SH20RAJ?tab=repositories)