mcpField
S
scaffolding-nestjs-app

scaffolding-nestjs-app

github.com/satanlittlehelper開発061 回インストール2 日前に更新
リポジトリ

scaffolding-nestjs-app について

空のディレクトリや既存のモノレポで NestJS サービスを初期構築するスキルです。ログ、API 動作、テスト、デプロイの基準を整え、環境に応じた構成選択を案内します。

主な機能

  1. 01Nest CLIとNx環境の判別
  2. 02Pinoロギングの基準設定
  3. 03統一APIレスポンスと例外処理
  4. 04カバレッジ付きVitest設定
  5. 05Docker、Compose、Makefileの土台

活用シーン

  1. 01NestJSサービスの新規作成
  2. 02NxモノレポへのNestJSアプリ追加
  3. 03テストとデプロイの基盤構築

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/satanlittlehelper/ai-dev-kit --skill scaffolding-nestjs-app

インストール後、Claude Code で/scaffolding-nestjs-appを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

templates/docker/
.env.example.standalone
docker-compose-service-snippet.monorepo.md
docker-compose.standalone.yml
dockerfile-monorepo-reference.md
Dockerfile.standalone
env-example-snippet.monorepo.md
Makefile.monorepo-reference
Makefile.standalone
templates/docs/
claude-md-nestjs-snippet.md
claude-md-skills-workflow-snippet.md
gitignore-ai-artifacts-snippet.txt
templates/eslint/
.prettierignore
.prettierrc
eslint.app.config.mjs
eslint.base.config.mjs
templates/husky/
lintstagedrc.nx.js
lintstagedrc.standalone.js
pre-commit
pre-push.nx
pre-push.standalone
templates/pino/
app-logger.module.ts
pino-config.constants.ts
pino-config.helpers.ts
pino-config.loader.ts
pino-config.types.ts
templates/response/
api-exception-filter.ts
api-response.interceptor.ts
api.types.ts
templates/vitest/
vitest.config.ts
vitest.setup.ts

詳細

新しい NestJS アプリをスキャフォールディングする

概要

空のディレクトリ(または既存の Nx モノレポ内の新しいアプリ)を、作成者標準のベースラインを備えた NestJS サービスに変換します。ベースラインには、pino ロギング、統一された ApiResponse 契約とグローバル例外フィルター、ESLint/Prettier、カバレッジ付き Vitest、husky のコミット前/プッシュ前フック、Docker/Compose/Makefile のデプロイスキャフォールディングが含まれます。テンプレートはこのファイルと同じ場所の templates/ にあります。記憶を頼りに書き直すのではなく、コピーして調整してください。

使用する場面

  • ユーザーから、NestJS アプリ/サービス/マイクロサービスをゼロから作成するよう依頼された場合。
  • ユーザーから、既存の Nx(またはその他の)モノレポに新しいバックエンドアプリを追加するよう依頼された場合。
  • すでにスキャフォールディング済みのアプリに単一の機能/モジュールを追加する場合には使用しないでください。このスキルの対象は初期ブートストラップのみです。

ステップ 0 — 環境を検出する

digraph detect_env {
    "nx.json exists in target repo root?" [shape=diamond];
    "Any existing app/project in the repo?" [shape=diamond];
    "Use Nx generator" [shape=box];
    "Ask user: fresh Nx workspace or plain Nest CLI app?" [shape=diamond];
    "Use Nest CLI (nest new / nest generate)" [shape=box];
    "npx create-nx-workspace, then Nx generator" [shape=box];

    "nx.json exists in target repo root?" -> "Use Nx generator" [label="yes"];
    "nx.json exists in target repo root?" -> "Any existing app/project in the repo?" [label="no"];
    "Any existing app/project in the repo?" -> "Use Nest CLI (nest new / nest generate)" [label="yes"];
    "Any existing app/project in the repo?" -> "Ask user: fresh Nx workspace or plain Nest CLI app?" [label="no"];
    "Ask user: fresh Nx workspace or plain Nest CLI app?" -> "npx create-nx-workspace, then Nx generator" [label="Nx"];
    "Ask user: fresh Nx workspace or plain Nest CLI app?" -> "Use Nest CLI (nest new / nest generate)" [label="plain Nest"];
}
  • nx.json がある場合 → npx nx g @nx/nest:application apps/<app-name>(アプリ名/パスが指定されていない場合は確認してください。プラグインがまだインストールされていない場合は、先に @nx/nest を devDependencies に追加してください)。
  • nx.json はないが、リポジトリにすでに少なくとも 1 つのアプリ/パッケージがある場合 → Nx 以外の複数アプリリポジトリです。既存アプリの構成に従って、nest new <app-name> または nest generate を使用してください。Nx を使用していないリポジトリに、黙って Nx を導入しないでください。
  • nx.json がなく、既存のアプリもまったくない場合(空/新規リポジトリ) → これは「プロジェクトで最初のアプリ」のケースです。Nx ワークスペース(npx create-nx-workspace@latest を実行後、上記の Nx ジェネレーターを使用)をセットアップするか、プレーンなスタンドアロン Nest CLI アプリ(npx @nestjs/cli new .)にするかを、必ずユーザーに確認してください。どちらかを決めつけないでください。この選択によって、その後のすべての手順(使用するテンプレートの種類:*.standalone.* と Nx/モノレポ用のもの)が決まります。

ステップ 1 — 依存関係をインストールする

このスキルに必要な一覧です。最新バージョン(@latest)をインストールし、手動でバージョンを固定しないでください。モノレポでは、まずワークスペースルートの package.json を確認し、実際に不足しているものだけを追加してください。ESLint/Vitest ツールの多くは、通常すでに含まれています。

依存関係:

npm install nestjs-pino pino pino-http class-validator class-transformer

例外フィルターの axios エラー分岐を使用する場合は axios も追加してください(ステップ 5 を参照)。それ以外の場合は追加しないでください。

開発依存関係:

npm install -D pino-pretty eslint prettier eslint-config-prettier eslint-plugin-import eslint-plugin-prettier typescript-eslint vitest @vitest/coverage-v8 unplugin-swc vite-tsconfig-paths husky lint-staged

モノレポの場合のみ、ワークスペースの依存関係にまだ含まれていなければ @nx/eslint-plugin も追加してください。

ステップ 2 — 生成後のクリーンアップ:TypeScript の厳格モード、Jest と e2e のひな型を削除

このスタックのすべてのアプリでは、厳格モードがデフォルトです。ジェネレーターが設定したと思い込まず、必ず確認してください。 アプリの tsconfig.json(単独構成の場合)またはワークスペースの tsconfig.base.json(モノレポの場合)を開き、"compilerOptions"."strict": true が設定されていることを確認します。nest new のデフォルトテンプレートではすでに設定されていますが、@nx/nest:application はワークスペースの tsconfig.base.json を継承します。この設定ファイルがこの規約より前に作られている場合があるため、設定がなければ明示的に追加してください。ひな型の作成中に型エラーを回避するため、個別の厳格モード用フラグ(strictNullChecks、noImplicitAny など)を無効にしないでください。根本原因となっている型を修正してください。

nest new と @nx/nest:application は、どちらもデフォルトで Jest をひな型として生成します。この筆者のスタックで使うのは Vitest のみで、独立した e2e テストターゲットはありません。アプリの生成直後、ほかの作業に手を付ける前に、次のファイルや設定を削除してください。

  • jest.config.ts/jest.config.js(アプリレベルのもの、および新規の単独リポジトリではルートのものも)
  • test/(生成された e2e フォルダー。通常は app.e2e-spec.ts と jest-e2e.json が含まれます)
  • ソースと一緒に Jest が生成した *.spec.ts ファイル(例:src/app.controller.spec.ts)。Vitest に移植せず、削除してください。実際にテスト対象となるロジックができたら、このプロジェクト独自のテスト規約に従って、後から実際の spec ファイルを作成します。
  • Jest 関連の devDependencies:jest、@nestjs/testing(ほかの用途がなければ。ここでは使用しません)、ts-jest、@types/jest、jest-environment-node(ジェネレーターによって追加されるものが正確に異なるため、package.json を確認してください。Nx/Nest のジェネレーターのバージョンによって具体的な一覧は異なるので、追加されたものをすべて削除します)。
  • package.json に埋め込まれた jest 設定ブロック(Nx の @nx/nest:application と通常の nest new は、どちらもトップレベルに "jest" キーを追加します)、および Jest を指定する test/test:e2e/test:watch/test:cov npm スクリプト。これらはステップ 7 で Vitest 相当の設定に置き換えます。
  • Nx モノレポのアプリの場合:@nx/jest:jest を参照する、生成された project.json の test/e2e ターゲットも削除します。ステップ 8 で、ワークスペース内のほかのアプリに合わせて、これらを nx:run-commands と vitest run に置き換えます。

Vitest/tsc の設定:spec ファイルによって本番ビルドや IDE が失敗しないようにします。

  1. tsconfig.app.json の "include" の隣に "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"] を追加します。これを追加しないと、本番ビルドが spec ファイルを取り込み、vi など Vitest 専用のグローバルが原因で失敗します。
  2. tsconfig.spec.json を作成します:{ "extends": "./tsconfig.json", "compilerOptions": { "types": ["vitest/globals"] }, "include": ["src/**/*.spec.ts", "src/**/*.test.ts", "vitest.config.ts"] }。これがないと、Vitest 自体は問題なく実行できても、spec ファイル内の Cannot find name 'vi' が IDE と tsc に報告されます。
  3. すべての spec ファイルで、グローバルのみに依存せず、'vitest' から vi を明示的にインポートします。IDE が tsconfig.spec.json の型を解決するのは、実際にその設定を使っている場合に限られます。明示的にインポートすれば、設定にかかわらず動作します。

アプリの基本設定:設定、グローバルプレフィックス、バリデーション

ステップ 3 の前に、ジェネレーターの出力にかかわらず、main.ts/app.module.ts が次の基本設定を備えていることを確認します。グローバルな ConfigModule で PORT を検証すること(Joi または class-validator を使用し、使用箇所では getOrThrow を使います。単独の process.env.PORT は使用しません)、app.setGlobalPrefix('api')、transform: true と whitelist: true を設定し、暗黙的な変換を有効にしたグローバルな ValidationPipe、および class-validator のカスタムデコレーターで Nest DI を使えるようにする useContainer(app, { fallbackOnErrors: true }) です。アプリが、サービス名のプレフィックスを取り除いてから転送する Traefik(または同等のリバースプロキシ)の背後にある場合、アプリ独自のルートは通常の /api/... のままにします。プロキシのルートパターン(例:ストリッププレフィックスのミドルウェアを使う /{service_name}/api)はプロキシ側の責任であり、アプリ内部で把握する必要はありません。

アプリの基本設定:プロセスガード

各アプリの main.ts は、リクエストチェーンの外で発生する障害にも耐えられる必要があります。これがないと、未処理の拒否や例外によって、pino を経由せず生のスタックトレースが stderr に出力されてプロセスが終了します。また、SIGTERM では Nest のシャットダウンフックが実行されないため、Redis/BullMQ/DB の接続が閉じられません。共有ヘルパーから取得する次の2つの関数を呼び出します。モノレポでは @chatbot-platform/helpers/nest の registerProcessGuards/runBootstrap を使い(重複して実装しないでください)、スタンドアロンアプリでは同じ2つの関数を src/common/process-guards/ に作成します。main.ts に process.on(...) を直接記述してはいけません。

  • runBootstrap(bootstrap) は、main.ts の末尾にある単独の bootstrap()/void bootstrap() 呼び出しを置き換えます。bootstrap が拒否された場合は console.error に出力され(pino の準備がまだできていない可能性があります)、プロセスは終了コード 1 で終了します。
  • registerProcessGuards({ app, logger: app.get(Logger) }) は、app.useLogger(app.get(Logger)) の直後に呼び出します。app.enableShutdownHooks() を実行し、unhandledRejection/uncaughtException が発生したときは pino を通じてログを記録し、名前付き定数で指定したタイムアウトと競合させながら app.close() を実行した後、process.exit(1) を呼び出します。シャットダウン中に繰り返し発生したイベントは無視されます。どちらのイベントも同じように処理します。コンテナの再起動ポリシーによってサービスは復旧しますが、「稼働中だが壊れている」プロセスを残すより、再起動するほうが適切です。

アプリで @nestjs-modules/ioredis または BullMQ の @Processor を登録する場合は、pino を通じてログを記録する error リスナーも追加します(プロセッサーには @OnWorkerEvent('error')、または @InjectRedis() を使って redis.on('error', ...) を呼び出す小さなモジュールを使用します)。そうしないと、ioredis と BullMQ のエラーは pino を経由せず、console.error に出力されます。

ステップ 3 — API バージョニングについて確認する

main.ts を作成する前に、新しいアプリで URI API バージョニングが必要かどうかを必ずユーザーに確認してください。どちらかに決めつけてはいけません。このスタックでは有効にしているアプリ(ext-gate)もあれば、有効にしていないアプリ(bot-gateway、miniapp-gate、bot-publisher)もあります。これはアプリごとの判断であり、一律の規約ではありません。

必要な場合は、main.ts に追加します(setGlobalPrefix の後、useContainer の前)。

import { VersioningType } from '@nestjs/common';

app.enableVersioning({
  type: VersioningType.URI,
  defaultVersion: '1',
});

これにより、ルートは /api/v1/... として解決されます。不要な場合は追加せず、ルートは /api/... のままにします。

ステップ 4 — pino ロガーを組み込む

templates/pino/ ディレクトリ全体を新しいアプリにコピーします。これは相互に連携する5つのファイルであり、単一のモノリシックなモジュールではありません。

テンプレートファイル役割
pino-config.types.tsAppLoggerOptions、AppLoggerContextFilter、PinoConfig と、それぞれの is* 型ガード
pino-config.constants.tsREDACT_AUTH_HEADER_PATHS(認証/トークンのヘッダーをデフォルトで秘匿)、PINO_CONFIG_FILENAME
pino-config.helpers.tsbuildPinoParams — pretty-transport、秘匿するパス、コンテキストのブラックリストフックを構築する純粋なビルダー
pino-config.loader.tsloadPinoConfig — PINO_CONFIG_ENABLED=true の場合、cwd から省略可能な pino.config.json を読み込みます
app-logger.module.ts上記4つを組み合わせる薄い nestjs-pino ラッパー、AppLoggerModule.forRoot/forRootAsync

配置場所:

  • すでに複数の Nest アプリでコードを共有しているモノレポ → 共有ライブラリがまだなければ、ディレクトリ全体を共有ライブラリ(例:libs/nest-logger)に配置します。すでにある場合は、それを依存先として使用し、重複して実装しないでください。
  • スタンドアロンアプリ、またはモノレポの最初のアプリ → アプリ内の src/common/logger/ に、同じ5つのファイルを配置します。 各オプションの制御内容(buildPinoParamsが受け取るAppLoggerOptions):
  • level — pino のログレベル('info'、'debug'、…)。
  • pretty — 開発モードのpino-prettyトランスポート(colorize: true、translateTime: 'SYS:standard')。本番環境ではオフです。— NDJSONは下流のログ転送ツールにそのまま渡され、色付けや整形された出力はノイズになるだけです。
  • singleLine — pino-pretty独自のsingleLineオプションにそのまま渡されます。trueなら、大きなreq/resペイロードがあってもリクエストごとにログ1行を維持し、falseなら、深くネストされたオブジェクトをローカルで読みやすいように複数行で整形します。
  • pinoConfigEnabled — trueの場合、loadPinoConfigはprocess.cwd()にある、{ contextFilter?: { blacklist: string[] }, redactPaths?: string[] }という形式のpino.config.jsonを必須とし、ファイルが存在しないか形式が不正であれば、説明付きのエラーをスローします。false(デフォルト)の場合、ファイルは読み込まれず、組み込みの秘匿対象パスのみが適用されます。共有ライブラリをプロジェクトごとに手作業で編集するのではなく、この回避手段を使って、プロジェクト固有のノイズが多いケース(ヘルスチェックモジュール独自のcontextが開発ログを埋め尽くす場合や、組み込みの秘匿対象リストでカバーされないフィールドなど)に対応してください。

LOG_LEVEL/LOG_PRETTY/LOG_PRETTY_SINGLE_LINE/PINO_CONFIG_ENABLEDには、4つすべてで同じJoiスキーマ形式を選び、一貫して使用してください。 どちらの形式も機能します。問題は混在させることです。

  • 文字列形式(Joi.string().empty('').valid('true', 'false').default('true')。config.get<string>('LOG_PRETTY') === 'true'として比較)— このスキルのデフォルト形式です。同じ環境変数の値をConfigServiceの外部(たとえばシェルスクリプトへの出力)でも読み取る可能性がある場合に必要です。ネイティブのブール値はシリアライズ後に保持できないためです。
  • ブール値形式(Joi.boolean().default(false)。config.getOrThrow<boolean>('LOG_PRETTY')として読み取り)— Nest独自のConfigService以外で生の値を必要としない場合は、こちらのほうが簡潔です。

どちらの形式を選ぶ場合も、4つのキーすべてに同じ形式を適用してください。 混在させることこそ、この警告がある理由となる実際の不具合です。LOG_PRETTY: Joi.boolean()を宣言しながら、config.get<string>('LOG_PRETTY') === 'true'として比較すると、検証エラーは発生せず、ログの出力が誤った状態で、結果は暗黙的にfalseになります。実際のbooleanは文字列'true'と===にならないためです。キーごとに、スキーマで宣言した型とファクトリーでの読み取り方法を一致させてください。

文字列形式の例:

LOG_LEVEL: Joi.string().default('info'),
LOG_PRETTY: Joi.string().empty('').valid('true', 'false').default('true'),
LOG_PRETTY_SINGLE_LINE: Joi.string().empty('').valid('true', 'false').default('true'),
PINO_CONFIG_ENABLED: Joi.string().empty('').valid('true', 'false').default('false'),

app.module.tsに組み込みます(文字列形式のファクトリーです。ブール値形式を使う場合は、すべての=== 'true'/config.get<string>をconfig.getOrThrow<boolean>に置き換えてください):

AppLoggerModule.forRootAsync({
  imports: [ConfigModule],
  inject: [ConfigService],
  useFactory: (config: ConfigService): AppLoggerOptions => ({
    level: config.getOrThrow<string>('LOG_LEVEL'),
    pretty: config.get<string>('LOG_PRETTY') === 'true',
    singleLine: config.get<string>('LOG_PRETTY_SINGLE_LINE') === 'true',
    pinoConfigEnabled: config.get<string>('PINO_CONFIG_ENABLED') === 'true',
  }),
}),

また、main.tsでは、NestFactory.create(AppModule, { bufferLogs: true })を実行し、次にapp.useLogger(app.get(Logger))(nestjs-pinoのLogger)を呼び出してから、registerProcessGuards({ app, logger: app.get(Logger) })を呼び出します(「App Baseline: Process Guards」を参照)。

環境変数 — このリポジトリ独自の環境変数に関する規約に従い、3か所すべてを更新してください(上記のJoiスキーマ、ルートの.env.example、そしてdevops/デプロイ層があるモノレポの場合は、devops/.env.exampleと対応するdocker-compose.prod.ymlサービスの環境変数ブロック):LOG_LEVEL、LOG_PRETTY、LOG_PRETTY_SINGLE_LINE、PINO_CONFIG_ENABLED。いずれかを変更する特別な理由がない限り、すべての箇所でLOG_PRETTY_SINGLE_LINE=true、PINO_CONFIG_ENABLED=falseをデフォルトにしてください。

組み込み後は、実際にアプリを起動し(.envでLOG_PRETTY=trueを設定)、色付きの1行出力が表示されることを確認してから先に進んでください。生のNDJSONではありません。prettyフラグが暗黙的にfalseになっていても、そうしないと見落としやすいためです。

Step 5 — 例外フィルターと API レスポンスインターセプターを接続する

まず、重複がないか確認します(この手順は最も省略されがちです)。新しいものを作成する前に、対象のリポジトリを検索し、既存の ExceptionFilter/レスポンスインターセプターの実装がないか確認してください。

  • 見つかった場合で、リポジトリがモノレポの場合 → 共有ライブラリに切り出します(このスキル自体の履歴を参考にしてください。まさにこの切り出しを行った例として、@chatbot-platform/helpers/nest を参照)。既存のアプリと新しいアプリの両方が、共有バージョンに依存するようにします。異なる実装のコピーを2つ残さないでください。
  • 見つかった場合で、リポジトリがスタンドアロンの場合 → そのまま再利用し、2つ目を作成しないでください。
  • 見つからない場合 → templates/response/api.types.ts、api-exception-filter.ts、api-response.interceptor.ts をコピーします。ファイルの配置先に合わせて、../types/api.types のインポートパスを修正してください。アプリで axios を使って他の HTTP API を呼び出す理由がない場合は、フィルターから isAxiosError 分岐と axios のインポートを削除してください。

app.module.ts のプロバイダーに登録します。main.ts の useGlobalFilters/useGlobalInterceptors は使わないでください。また、他の APP_INTERCEPTOR エントリーがある場合、ApiResponseInterceptor を最後に配置して、それらの出力をラップするようにします。

providers: [
  { provide: APP_INTERCEPTOR, useClass: ApiResponseInterceptor },
  { provide: APP_FILTER, useClass: ApiExceptionFilter },
],

Step 6 — ヘルスエンドポイントを接続する

このスタックで新しく作るすべてのアプリに、依存関係のないシンプルな稼働確認エンドポイントを追加します。ユーザーから明示的な依頼がない限り、@nestjs/terminus や DB/Redis の準備状態チェックは使いません。実装パターン(テンプレートファイルからコピーせず、インラインで記述します。直接書けるほど小さいためです):

src/app/app.controller.ts:

import { Controller, Get } from '@nestjs/common';
import { Public } from '<path-to-shared-public-decorator-or-local-equivalent>';

import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Public()
  @Get('health')
  getHealth(): { service: string; status: string } {
    return this.appService.getHealth();
  }
}

src/app/app.service.ts(または、テスト可能なサービスに対してアプリですでにドット記法のサブフォルダー規約を使用している場合は src/app/app.service/app.service.ts):

import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  getHealth(): { service: string; status: string } {
    return {
      service: '<app-name>',
      status: 'ok',
    };
  }
}

app.module.ts に両方を登録します。controllers: [AppController] を指定し、providers に AppService を追加してください。Public デコレーターを付けると、そのルートはグローバル認証ガード(APP_GUARD)によるチェックをスキップします。アプリのガードが Reflector/メタデータを使ったパブリックルートのバイパスにまだ対応していない場合は、まずガードにその処理を追加してください(認証ロジックの前に Reflector.getAllAndOverride を確認する箇所については、ガード自身の canActivate を参照してください)。結果のパスは /api/health です。このアプリで Step 3(バージョニング)を有効にした場合は /api/v1/health になります。

Step 7 — ESLint + Prettier

templates/eslint/eslint.base.config.mjs をリポジトリのルートにコピーします(既存の設定がある場合は上書きせず、統合してください。カスタマイズされたベース設定を上書きしないでください)。スタンドアロン(非 Nx)アプリでは、@nx/enforce-module-boundaries ブロックと、apps/*/libs/* の tsconfig glob パスを削除します。削除対象の詳細は、テンプレート内のコメントを参照してください。

モノレポのアプリでは、templates/eslint/eslint.app.config.mjs もコピーし、__APP_NAME__ を実際のアプリ名に置き換えて、apps/<app-name>/eslint.config.mjs として配置します。

templates/eslint/.prettierrc と templates/eslint/.prettierignore をリポジトリのルートにコピーします(両方ともすでに存在する場合はスキップし、カスタマイズされた設定を上書きしないでください)。eslint.base.config.mjs は prettier/prettier: 'error' を強制するため、.prettierrc と ESLint ルールのフォーマットが一致しないと、lint が失敗します。

Step 8 — Vitest

templates/vitest/vitest.config.ts と templates/vitest/vitest.setup.ts を新しいアプリにコピーします(vitest.config.ts はアプリのルートに、src/test/vitest.setup.ts はそのパスに配置します)。coverage.exclude のリストはそのまま残してください。これは、*.api.service.ts、*.repository.ts、DTO、モジュール、コントローラー、例外フィルターを単体テストの対象外とする、作成者の規約を反映しています(ロジックを含まない接着層、または間接的にすでにカバーされているものです)。まだテストがない新しいアプリでは、thresholds はコメントアウトしたままにしてください。プロジェクトに十分なカバレッジができたらコメントを解除し、カバレッジ 80% のしきい値によって最初のコミットを妨げないようにします。

ステップ 9 — pre-commit/pre-push フック

.husky/ がまだ存在しない場合は、npx husky init を実行します。以下をコピーします。

  • templates/husky/pre-commit → .husky/pre-commit(どちらの構成でも同じです)。
  • モノレポ:templates/husky/pre-push.nx → .husky/pre-push、templates/husky/lintstagedrc.nx.js → .lintstagedrc.js(ルートに配置し、全アプリで共有します。アプリごとに複製しないでください)。
  • スタンドアロン:templates/husky/pre-push.standalone → .husky/pre-push、 templates/husky/lintstagedrc.standalone.js → .lintstagedrc.js。

両方のフックファイルに実行権限を付与します(chmod +x .husky/pre-commit .husky/pre-push)。

ステップ 10 — Docker/docker-compose/Makefile/.env.example

  • スタンドアロンアプリ:templates/docker/Dockerfile.standalone → devops/Dockerfile、 templates/docker/docker-compose.standalone.yml → devops/docker-compose.yml、 templates/docker/Makefile.standalone → Makefile、templates/docker/.env.example.standalone → .env.example。
  • 共有パラメーター化 Dockerfile(ARG APP_NAME)がすでにあるモノレポ →
    templates/docker/dockerfile-monorepo-reference.md を読み、既存の devops/Dockerfile がその形式に一致することを確認します。新しい Dockerfile は作成せず、 templates/docker/docker-compose-service-snippet.monorepo.md をひな形としてサービスブロックだけを追加し(__APP_NAME__/ __APP_NAME_UPPER__ プレースホルダーを置き換え)、templates/docker/env-example-snippet.monorepo.md に従って新しい環境変数を追加します。既存のルート Makefile は変更せずに再利用します。
  • 最初のアプリで、まだ devops/ の設定がないモノレポ →
    templates/docker/dockerfile-monorepo-reference.md の参照ブロックから devops/Dockerfile を作成し、 templates/docker/Makefile.monorepo-reference からルートの Makefile を作成します(__PROJECT_NAME__ をリポジトリ名に置き換えます)。この Makefile の prod-*/dev0-* ターゲットは docker-compose.networks.brandnew.yml/.existing.yml と docker-compose.base.yml を参照します。これらは、ソースプロジェクト固有の新規/既存 Docker ネットワークのデプロイトポロジーを表すため、新しいリポジトリでも同じ分岐が必要な場合にのみ追加してください。それ以外の場合は、_BNN/_EXN 変数を削除し、COMPOSE_PROD ターゲットが docker-compose.prod.yml を直接参照するようにします。

ステップ 11 — モノレポ:ルートの package.json に start スクリプトを追加する

モノレポのみ(スタンドアロンアプリではスキップします。npm run start がすでにアプリ独自のエントリーポイントです)。ワークスペースのルート package.json に start:<app-name> スクリプトを追加し、既存の規則に合わせます(例: "start:ext-gate": "nx run ext-gate:serve")。これがないと、新しいアプリには、ほかのアプリの start:* スクリプトと並ぶ、ローカル実行用の説明済みエントリーポイントがありません。

ステップ 12 — 対象リポジトリに規約を記載する

templates/docs/claude-md-nestjs-snippet.md の内容を(__APP_NAME__ を置き換え、パスを調整して)対象リポジトリの CLAUDE.md および/または AGENTS.md に、既存の規約の隣に追記します。どちらのファイルもまだない場合は、リポジトリのルートに CLAUDE.md を作成し、これを初期内容として使用します。

また、templates/docs/claude-md-skills-workflow-snippet.md も追記します。これはスタックに依存しない内容(スキル/サブエージェントの使い方、プランモード、コミットの規律、ワークフロー成果物の保存場所)であり、NestJS プロジェクトだけでなく、すべてのプロジェクトの CLAUDE.md に追加します。対象リポジトリの CLAUDE.md に同等のスキル/ワークフロー規約がすでに記載されている場合のみスキップしてください。重複させないでください。

ステップ 13 — .gitignore に AI の成果物を追加する

templates/docs/gitignore-ai-artifacts-snippet.txt を対象リポジトリの .gitignore に追記します(ファイルがない場合は作成します)。このスニペットで無視するのは、個人用の Claude Code ファイル(CLAUDE.local.md、settings.local.json、worktrees、.doc-review/)のみです。ステップ 11 で追加した CLAUDE.md/AGENTS.md はプロジェクト共有ファイルなので、引き続き追跡対象にします。追記前に既存の記述を確認し、すでにある行は重複させないでください。

クイックリファレンス — テンプレートファイル

ファイル目的単独構成の場合のみ必要な編集
templates/pino/app-logger.module.tsnestjs-pino のラッパーモジュール(以下の4ファイルを組み合わせます)なし
templates/pino/pino-config.types.tsAppLoggerOptions/PinoConfig の型と型ガードなし
templates/pino/pino-config.constants.tsデフォルトの redact パス、pino.config.json のファイル名なし
templates/pino/pino-config.helpers.tsbuildPinoParams の純粋なビルダーなし
templates/pino/pino-config.loader.tsオプションの pino.config.json ローダーなし
templates/response/api.types.tsApiResponse<T> の契約なし
templates/response/api-exception-filter.tsグローバルな @Catch() フィルター未使用なら axios 分岐を削除
templates/response/api-response.interceptor.tsコントローラーの戻り値をラップなし
templates/eslint/eslint.base.config.mjsルートのフラット ESLint 設定Nx の境界ルールを削除し、tsconfig パスを glob 化
templates/eslint/eslint.app.config.mjsアプリごとの ESLint オーバーライドモノレポのみ
templates/eslint/.prettierrc + .prettierignorePrettier のフォーマットルールなし
templates/vitest/vitest.config.ts + vitest.setup.tsテストランナーとカバレッジなし
templates/husky/pre-commitlint-staged フックなし
templates/husky/pre-push.nx / pre-push.standalonepush 時のテストフックどちらかを選択
templates/husky/lintstagedrc.nx.js / lintstagedrc.standalone.jslint-staged の設定どちらかを選択
templates/docker/Dockerfile.standalone / dockerfile-monorepo-reference.mdマルチステージビルドどちらかを選択
templates/docker/docker-compose.standalone.yml / docker-compose-service-snippet.monorepo.mdデプロイ設定どちらかを選択
templates/docker/Makefile.standalone / Makefile.monorepo-referencelocal-up/down/clean ターゲットどちらかを選択
templates/docker/.env.example.standalone / env-example-snippet.monorepo.md環境変数のベースラインどちらかを選択
templates/docs/claude-md-nestjs-snippet.md対象リポジトリ向けの NestJS 固有の規約をまとめたものパスを調整
templates/docs/claude-md-skills-workflow-snippet.mdスタックに依存しないスキル/サブエージェントのワークフロールールなし
templates/docs/gitignore-ai-artifacts-snippet.txt個人用の Claude Code ファイル(CLAUDE.local.md、settings.local.json、worktrees)を除外なし

よくある間違い

間違い修正方法
main.ts で app.useGlobalFilters() を使ってフィルター/インターセプターを登録する代わりに app.module.ts の APP_FILTER/APP_INTERCEPTOR プロバイダー経由で登録します
ApiResponseInterceptor を他の APP_INTERCEPTOR より前に置く他のインターセプターの出力をラップするため、最後に置く必要があります
モノレポに既存の例外フィルター/インターセプターがあるのに、別のものを作成する既存のものを共有ライブラリに切り出して再利用します
Nx の @nx/enforce-module-boundaries ESLint ルールを単独構成のアプリにコピーする削除します。適用すべき libs/apps の境界はありません
初日からカバレッジのしきい値を厳しく設定するアプリに実際のテストが追加されるまで、thresholds はコメントアウトしたままにします
すでに共有のパラメーター化された Dockerfile があるモノレポで、新しいアプリごとの Dockerfile を作成するARG APP_NAME を使う共有の devops/Dockerfile を再利用し、compose のサービスブロックだけを追加します
必須の3か所のうち1か所だけに新しい環境変数を追加するJoi スキーマ + ルート .env.example + devops/.env.example(+ compose の environment: ブロック)に毎回すべて追加します
registerProcessGuards を使わず、main.ts を単独の bootstrap() / void bootstrap() で終えるrunBootstrap(bootstrap) でラップし、useLogger の直後に registerProcessGuards を呼び出します。「アプリのベースライン:プロセスガード」を参照してください
アプリが「内部向け」だからとヘルスエンドポイントを省略するすべての HTTP アプリに GET /health(グローバルプレフィックスがある場合は /api/health)を設けます。内部専用アプリでも、オーケストレーションの稼働確認に必要です

コメント