Redis
 Computer >> コンピューター >  >> プログラミング >> Redis

Vercel EdgeとUpstash RedisでNext.jsにレート制限を実装する方法

Vercel Edge Middlewareと@upstash/ratelimitライブラリを活用して、Webアプリケーションにレート制限(レートリミット)を実装する方法を解説します。@upstash/ratelimitは、バックエンドでRedisを使用してレート制限データの保存・管理を行うライブラリです。

Vercel Edgeを使うメリット

Vercel Edgeは、ユーザーに最も近いロケーションで計算処理を実行するコンピューティングプラットフォームです。本記事では、リクエストがバックエンドに到達する前に処理を割り込ませるVercel Edge Middlewareを使用します。レート制限の実装に最適な理由は以下の通りです。

  • バックエンドから分離されているため、トラフィックがバックエンドに到達する前にエッジロケーションでブロックできます。
  • 高速でコールドスタートの問題がないため、オーバーヘッドが最小限に抑えられます。
  • サーバーレス関数と比較してコスト効率に優れています。

@upstash/ratelimitを選ぶ理由

  • @upstash/ratelimitは、エッジ関数向けに特別に設計・テストされたレート制限ライブラリです。
  • マルチリージョンRedisをサポートしており、エッジロケーションからの低レイテンシを実現します。
  • Upstash Redisは、REST APIを備えているため、エッジ関数からアクセスできる唯一のマネージドRedisです。

それでは、実際の実装を見ていきましょう。

ステップ1:Redisのセットアップ

Upstash ConsoleまたはUpstash CLIでRedisデータベースを作成します。次のステップでUPSTASH_REDIS_REST_URLUPSTASH_REDIS_REST_TOKENが必要になるので、控えておいてください。

Vercel EdgeとUpstash RedisでNext.jsにレート制限を実装する方法

ステップ2:Next.jsのセットアップ

まず、Next.jsアプリケーションを作成します(他のフレームワークをお使いの場合は公式ドキュメントをご確認ください)。

npx create-next-app@latest --typescript

続いて、@upstash/ratelimitをインストールします。

npm i @upstash/ratelimit

プロジェクトフォルダのトップレベルにmiddleware.tsを作成します。

import { NextFetchEvent, NextRequest, NextResponse } from "next/server";
 
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
 
const redis = new Redis({
 url: "https://us1-merry-snake-32728.upstash.io",
 token: "AX_sAdsdfsgODM5ZjExZGEtMmmVjNmE345445kGVmZTk5MzQ=",
});
 
const ratelimit = new Ratelimit({
 redis: redis,
 limiter: Ratelimit.slidingWindow(5, "10 s"),
});
 
export default async function middleware(
 request: NextRequest,
 event: NextFetchEvent,
): Promise<Response | undefined> {
 const ip = request.ip ?? "127.0.0.1";
 const { success, pending, limit, reset, remaining } =
 await ratelimit.limit(ip);
 return success
 ? NextResponse.next()
 : NextResponse.redirect(new URL("/blocked", request.url));
}
 
export const config = {
 matcher: "/",
};

RedisのURLとトークンは、ご自身の環境のものに必ず置き換えてください。このコードではslidingWindowアルゴリズムを使用しており、同一IPアドレスからのリクエストを10秒間に5回まで許可しています。ユーザーIDやメールアドレスなど、ユーザー固有の識別子がある場合は、IPアドレスの代わりにそちらを使うことも可能です。レート制限に引っかかったリクエストは、middlewareによってblockedページへリダイレクトされます。

続いて、pages/blocked.tsxを作成しましょう。

import styles from "@/styles/Home.module.css";
 
export default function Blocked() {
 return (
 <div>
 <main className={styles.main}>
 <h3>Access blocked.</h3>
 </main>
 </div>
 );
}

これで実装は完了です。次のコマンドでアプリをVercelにデプロイしましょう。

vercel deploy

デプロイ後、ページを何度か更新してみてください。制限回数に達した時点で、blockedページへリダイレクトされることを確認できます。

キャッシュによるリモート呼び出しの削減

すべてのリクエストに対してリモート呼び出しを行うのは非効率です。@upstash/ratelimitパッケージには、エッジ関数が「ホット」な状態である限りデータをキャッシュする便利な機能があります。つまり、関数が「コールド」な状態のときだけRedisからデータを取得する仕組みになっており、リモート呼び出しの回数を大幅に減らせます。キャッシュを有効にするには、ratelimitオブジェクトをハンドラー関数middlewareの外側で宣言します。

レート制限アルゴリズムの選択

上記の例ではスライディングウィンドウ(sliding window)アルゴリズムを使用しました。用途に応じて、固定ウィンドウ(fixed window)やリーンバケット(leaking bucket)など、他のアルゴリズムを選択することもできます。それぞれの特徴や使い分けについては、公式ドキュメントをご確認ください。

マルチリージョンRedisの活用

エッジ関数からのアクセス性能をさらに高めたい場合は、複数のリージョンにRedisデータベースを配置するのが有効です。マルチリージョンRedisの構成方法についても、公式ドキュメントで詳しく解説されています。

参考リンク

  • Next.jsでのレート制限(非エッジ版)
  • Vercel Edge Middleware
  • Ratelimiting SDK
  1. 【Redis入門】SMEMBERSコマンドでセットの全要素を取得する方法

    このチュートリアルでは、redis-cliでSMEMBERSコマンドを使用して、指定したキーに保存されたセット(Set)型の値からすべての要素を取得する方法を解説します。 SMEMBERSは、セットに格納されている全メンバーを一括で取得できるシンプルかつ便利なコマンドです。それでは、基本的な使い方を見ていきましょう。 構文 redis host:port> SMEMBERS <keyname> 戻り値 配列(Array):セットに含まれるすべての要素を返します。 エラー:キーが存在するものの、そのキーに保存されている値がセット型ではない場合にエラーを返します。 使用例 ま

  2. QStashスケジューラでWhatsAppリマインダーを自動化する方法

    QStashは、サーバーレス環境向けに設計されたタスクスケジューラ兼メッセージキューのシステムです。状態管理を意識することなく、特定の間隔で1回だけ、あるいは繰り返し実行される自動化ワークフローを構築できるため、開発者にとって非常に心強いツールとなっています。 まずはQStashのコンソールを実際に触ってみて、リクエストの組み立て方を感覚的に掴んでみると良いでしょう。 ユースケース 私が開発しているプロダクト「GPT Hotline」は、いわばWhatsApp上で動くChatGPTです(同じ基盤モデルを使用しています)。ChatGPTアプリを使うときと同じ感覚で、専用の番号宛てにボットへメッ