← 解説「レート制限 — Throttler」に戻る
サンプルコードで身につける: レート制限 — Throttler
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1レート制限とは
一定時間内のリクエスト数に上限を設けるレート制限の考え方を図で示す例です。上限を超えたクライアントには 429 Too Many Requests を返す、という振る舞いを掴みます。
// レート制限 = 一定時間(ttl)あたりの回数(limit)に上限を設ける
//
// 設定: 60秒間に 5 回まで
//
// 1回目 → 200 OK
// 2回目 → 200 OK
// ...
// 5回目 → 200 OK
// 6回目 → 429 Too Many Requests ← 上限超過
// (60秒経つとカウントがリセットされ、また通るようになる)
//
// 目的: 総当たり攻撃や大量アクセス(DoS)の緩和
2ThrottlerModule のグローバル登録
ThrottlerModule を ttl と limit つきで登録し、ThrottlerGuard を全エンドポイントに適用する基本例です。APP_GUARD で登録すると、個別の指定なしに全ルートへ一律でレート制限が効きます。
import { Module } from "@nestjs/common";
import { APP_GUARD } from "@nestjs/core";
import { ThrottlerModule, ThrottlerGuard } from "@nestjs/throttler";
@Module({
imports: [
// 60秒間に最大10回。超えると 429 が返る
ThrottlerModule.forRoot([{ ttl: 60000, limit: 10 }]),
],
providers: [
// 全エンドポイントに一律適用
{ provide: APP_GUARD, useClass: ThrottlerGuard },
],
})
export class AppModule {}
3カウントの仕組みを再現する
Throttler が IP ごとにリクエスト回数を数え、時間枠を超えたらリセットする仕組みをプレーン TypeScript で再現した例です。「枠内の回数を数えて上限で弾く」という単純なカウンタが本質だと分かります。
TypeScript
4エンドポイント単位で上限を変える
@Throttle で特定のハンドラだけ厳しい上限にする例です。重い処理や攻撃されやすい口だけを個別に絞り込めるため、グローバル設定と組み合わせて使います。
import { Controller, Post } from "@nestjs/common";
import { Throttle, SkipThrottle } from "@nestjs/throttler";
@Controller("reports")
export class ReportsController {
// 重い集計処理は厳しめに: 60秒で2回まで
@Throttle({ default: { ttl: 60000, limit: 2 } })
@Post("export")
export() {
return { status: "started" };
}
// ヘルスチェックなど無害な口は制限から除外
@SkipThrottle()
@Post("ping")
ping() {
return { ok: true };
}
}
5ログイン試行のレート制限
総当たり攻撃の標的になりやすいログインエンドポイントに、厳しめのレート制限をかける実務例です。ハッシュ照合が重いログインは狙われやすいため、短時間の試行回数を絞ってブルートフォースを緩和します。
import { Controller, Post, Body, HttpCode } from "@nestjs/common";
import { Throttle } from "@nestjs/throttler";
@Controller("auth")
export class AuthController {
constructor(private readonly authService: AuthService) {}
// ログインは総当たりの標的。60秒で5回までに絞る
// 超えると 429 が返り、攻撃者の試行速度を大きく下げられる
@Throttle({ default: { ttl: 60000, limit: 5 } })
@Post("login")
@HttpCode(200)
async login(@Body() body: { email: string; password: string }) {
const user = await this.authService.validateUser(body.email, body.password);
return this.authService.issueToken(user);
}
}