Ccmmutty logo
Commutty IT
3 min read

Next.js の API で ip アドレスで rate limit をかける方法

https://cdn.magicode.io/media/notebox/blob_VrGWNE4

はじめに

Next.js の API routes で、一定期間内にリクエストを送れる回数を IP アドレスで制限(rate limit)する方法について調べたので、メモとして残しておきます。
↑ こちらの公式例を少し改良したものです。

ライブラリ インストール

yarn add lru-cache request-ip
yarn add -D @types/lru-cache @types/request-ip

コード

import LRU from "lru-cache";

import type { NextApiResponse } from "next";

type CheckLimitFunc = () => {
  check: (res: NextApiResponse, limit: number, ipAddress: string) => Promise<void>;
};
export const LimitChecker: CheckLimitFunc = () => {
  const tokenCache = new LRU<string, number>({
    max: 500, // Max 500 users per interval
    maxAge: 1000 * 60 * 5, // 5分,
  });

  return {
    check: (res, limit, token): Promise<void> =>
      new Promise((resolve, reject) => {
        const tokenCount = tokenCache.get(token) || 0;

        const currentUsage = tokenCount + 1;
        tokenCache.set(token, currentUsage);

        const isRateLimited = currentUsage > limit;
        res.setHeader("X-RateLimit-Limit", limit);
        res.setHeader("X-RateLimit-Remaining", isRateLimited ? 0 : limit - currentUsage);

        return isRateLimited ? reject("Too Many Requests") : resolve();
      }),
  };
};
// pageExtensions の設定をしているので .page.ts になっています
import requestIp from "request-ip";

import type { NextApiRequest, NextApiResponse } from "next";

import { LimitChecker } from "@/lib/limitChecker";

const limitChecker = LimitChecker();

type Data = {
  text: string;
  clientIp: string;
};
export default async function handler(req: NextApiRequest, res: NextApiResponse<Data>): Promise<void> {
  const clientIp = requestIp.getClientIp(req) || "IP_NOT_FOUND";

  try {
    await limitChecker.check(res, 3, clientIp);
  } catch (error) {
    console.error(error);

    res.status(429).json({
      text: `Rate Limited`,
      clientIp: clientIp,
    });
    return;
  }

  res.status(200).json({
    text: `テキスト`,
    clientIp: clientIp,
  });
}

おわりに

簡単にではありますが、API routes に対するリクエスト回数の制限をかける方法でした。 bot 等からの大量のリクエストに対する対策の一つにはなると思います 💪

参考にさせていただいたサイト

Discussion

コメントにはログインが必要です。