nestjs-best-practices について
NestJS開発者向けの実践ガイドです。公式ドキュメントを基に、モジュール境界、依存性注入、例外処理、DTO検証の方法をまとめ、Drizzle ORMの連携パターンも扱います。
主な機能
- 01ドメイン単位のモジュール設計
- 02依存性注入スコープの選択
- 03一貫した例外処理
- 04DTOと入力値の検証
- 05Drizzle ORMのリポジトリパターン
活用シーン
- 01NestJSモジュールの設計やリファクタリング
- 02注入とプロバイダースコープの設定
- 03APIへのDTO検証の追加
- 04例外処理やデータベースアクセスのレビュー
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ giuseppe-trisciuoglio/ developer-kit --skill nestjs-best-practicesインストール後、Claude Code で/nestjs-best-practicesを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/templates/controller-template.tsmodule-template.tsservice-template.tsreferences/api-validation-dto.mdarch-module-boundaries.mdarchitecture.mddb-drizzle-patterns.mddi-provider-scoping.mderror-exception-filters.md詳細
NestJS のベストプラクティス
概要
公式の NestJS ドキュメントに基づき、モジュール型アーキテクチャ、依存性注入のスコープ、例外フィルター、class-validator による DTO 検証、Drizzle ORM の統合パターンを徹底します。
使用する場面
- NestJS のモジュールや依存性注入を設計・リファクタリングするとき
- 例外フィルターの作成、DTO の検証、Drizzle ORM の統合を行うとき
- アンチパターンの有無をコードレビューするとき、または NestJS のコードベースに参加するとき
指示
1. モジュール型アーキテクチャ
厳格なモジュールのカプセル化に従います。各ドメイン機能はそれぞれ独立した @Module() にします。
- 他のモジュールが必要とするものだけをエクスポートし、内部のプロバイダーは非公開にします
- 循環依存に対する最後の手段としてのみ
forwardRef()を使用し、可能な限り構造を見直します - 関連するコントローラー、サービス、リポジトリを同じモジュール内にまとめます
- 横断的関心事(ロギング、設定、キャッシュ)には
SharedModuleを使用します
適用ルールについては、references/arch-module-boundaries.md を参照してください。
2. 依存性注入
ユースケースに応じて、適切なプロバイダースコープを選択します。
| スコープ | ライフサイクル | ユースケース |
|---|---|---|
DEFAULT | シングルトン(共有) | ステートレスなサービス、リポジトリ |
REQUEST | リクエストごとのインスタンス | リクエスト単位のデータ(テナント、ユーザーコンテキスト) |
TRANSIENT | 注入ごとに新しいインスタンス | ステートフルなユーティリティ、利用者ごとのキャッシュ |
DEFAULTスコープをデフォルトとし、正当な理由がある場合にのみREQUESTまたはTRANSIENTを使用します- コンストラクター注入のみを使用し、プロパティ注入は避けます
useClass、useValue、useFactory、useExistingを使ってカスタムプロバイダーを登録します
適用ルールについては、references/di-provider-scoping.md を参照してください。
3. リクエストライフサイクル
NestJS のリクエスト処理パイプラインを理解し、それに従います。
Middleware → Guards → Interceptors (before) → Pipes → Route Handler → Interceptors (after) → Exception Filters
- ミドルウェア:横断的関心事(ロギング、CORS、ボディ解析)
- ガード:認可と認証のチェック(
true/falseを返します) - インターセプター:レスポンスデータの変換、キャッシュの追加、処理時間の計測
- パイプ:入力パラメーターの検証と変換
- 例外フィルター:エラーレスポンスの捕捉と整形
4. エラー処理
アプリケーション全体でエラーレスポンスを標準化します。
- HTTP 固有のエラーには
HttpExceptionを拡張します - ドメイン固有の例外クラス(例:
OrderNotFoundException)を作成します - 一貫したエラー整形のために、グローバルな
ExceptionFilterを実装します - 想定されるビジネスロジックの失敗には Result パターンを使用します
- 例外を黙って握りつぶしてはいけません
適用ルールについては、references/error-exception-filters.md を参照してください。
5. バリデーション
API 境界で入力検証を徹底します。
transform: trueとwhitelist: trueを設定して、ValidationPipeをグローバルに有効化します- すべての DTO プロパティに
class-validatorのデコレーターを付与します - 型変換(
@Type()、@Transform())にはclass-transformerを使用します - 作成、更新、レスポンスの操作ごとに個別の DTO を作成します
- 生のユーザー入力を信頼せず、すべてを検証します
適用ルールについては、references/api-validation-dto.md を参照してください。
6. データベースパターン(Drizzle ORM)
NestJS のプロバイダー規約に従って Drizzle ORM を統合します。
- Drizzle クライアントを注入可能なプロバイダーでラップします
- データアクセスをカプセル化するためにリポジトリパターンを使用します
- 各ドメインモジュールの専用スキーマファイルでスキーマを定義します
- 複数ステップの操作にはトランザクションを使用します
- コントローラーにデータベースのロジックを記述しません
適用ルールについては、references/db-drizzle-patterns.md を参照してください。
ベストプラクティス
| 領域 | 実施すること | 避けること |
|---|---|---|
| モジュール | ドメイン機能ごとに1つのモジュールを作成する | すべてをAppModuleに詰め込む |
| DIのスコープ | デフォルトでシングルトンスコープにする | 正当な理由なくREQUESTスコープを使う |
| エラー処理 | カスタム例外フィルターとドメインエラーを使用する | console.logを使った単純なtry/catch |
| バリデーション | グローバルなValidationPipeとDTOデコレーターを使う | コントローラー内で手動のifチェックを行う |
| データベース | クライアントを注入したリポジトリパターンを使う | コントローラー内でDBクエリを直接実行する |
| テスト | サービスをユニットテストし、コントローラーをe2eテストする | テストを省略する、または実装の詳細をテストする |
| 設定 | 型付きスキーマで@nestjs/configを使う | 値をハードコードする、またはprocess.envを使う |
例
例:バリデーションを備えた新しいドメインモジュール
「Product」機能を構築する場合は、次のワークフローに従います。
1. 適切にカプセル化したモジュールを作成します:
// product/product.module.ts
@Module({
imports: [DatabaseModule],
controllers: [ProductController],
providers: [ProductService, ProductRepository],
exports: [ProductService], // Only export what others need
})
export class ProductModule {}
2. バリデーション済みのDTOを作成します:
// product/dto/create-product.dto.ts
import { IsString, IsNumber, IsPositive, MaxLength } from 'class-validator';
export class CreateProductDto {
@IsString() @MaxLength(255) readonly name: string;
@IsNumber() @IsPositive() readonly price: number;
}
3. エラー処理を備えたサービスを作成します:
@Injectable()
export class ProductService {
constructor(private readonly productRepository: ProductRepository) {}
async findById(id: string): Promise<Product> {
const product = await this.productRepository.findById(id);
if (!product) throw new ProductNotFoundException(id);
return product;
}
}
4. モジュールの登録を確認します:
# Check module is imported in AppModule
grep -r "ProductModule" src/app.module.ts
# Run e2e to confirm exports work
npx jest --testPathPattern="product"
制約と警告
- 正当な理由なくスコープを混在させないでください —
REQUESTスコープのプロバイダーは、すべての依存先に連鎖します - コントローラーからデータベースに直接アクセスしないでください — 必ずサービス層とリポジトリ層を経由します
forwardRef()は避けてください — モジュールを再構成して循環依存を解消しますValidationPipeを省略しないでください — DTOを使って、APIの境界で必ずバリデーションを行います- シークレットをハードコードしないでください — 環境変数を使って
@nestjs/configを設定します - モジュールの責務を絞ってください — 1つのモジュールにつきドメイン機能は1つとし、「巨大なモジュール」は避けます
参考資料
references/architecture.md— NestJSのアーキテクチャパターンを詳しく解説しますreferences/— 正しい例と誤った例を含む、個別の適用ルールassets/templates/— 一般的なNestJSコンポーネント向けのスターターテンプレート