Add rate limits to NextJS APIs

Sh Raj - Apr 9 - - Dev Community

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

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): πŸ“‚

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 βœ…
  }
}
Enter fullscreen mode Exit fullscreen mode

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: πŸ“

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',
    },
  });
};
Enter fullscreen mode Exit fullscreen mode
  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 || Twitter

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .