Dev Study
← 解説「レート制限 — 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);
  }
}