T-CREATOR

NestJS Decorator 速見表:Controller/Param/Custom Decorator の定型パターン

NestJS Decorator 速見表:Controller/Param/Custom Decorator の定型パターン

NestJS を使い始めると、様々な Decorator が登場して「これはどう使うんだっけ?」と迷うことはありませんか。

実は NestJS における Decorator は、API 開発の生産性を大きく左右する重要な要素です。この記事では、Controller、Param、そして Custom Decorator の定型パターンを体系的にまとめ、すぐに使える速見表としてお届けします。

普段の開発で「あのパターンどう書くんだっけ?」と思ったときに、サッと確認できる構成になっています。TypeScript の型定義も含めて、実践的なサンプルコードを豊富に用意しました。

NestJS Decorator 速見表

以下は、よく使う Decorator の一覧です。詳細は各セクションで解説します。

Controller Decorator

Decorator用途使用例
@Controller('path')ベースパスを定義@Controller('users')
@Get('path')GET リクエストを処理@Get(':id')
@Post('path')POST リクエストを処理@Post()
@Put('path')PUT リクエストを処理@Put(':id')
@Delete('path')DELETE リクエストを処理@Delete(':id')
@Patch('path')PATCH リクエストを処理@Patch(':id')
@HttpCode(code)ステータスコードを指定@HttpCode(204)
@Header(key, value)レスポンスヘッダーを設定@Header('Cache-Control', 'no-store')
@Redirect(url, code)リダイレクトを設定@Redirect('​/​new-path', 301)

Param Decorator

Decorator用途使用例取得データ
@Param(key?)パスパラメータを取得@Param('id') id: string​/​users​/​:idid
@Query(key?)クエリパラメータを取得@Query('page') page: string?page=1page
@Body(key?)リクエストボディを取得@Body() dto: CreateDtoPOST/PUT のボディ
@Headers(key?)リクエストヘッダーを取得@Headers('authorization') auth: stringAuthorization ヘッダー
@Req()Request オブジェクトを取得@Req() req: RequestExpress の Request
@Res()Response オブジェクトを取得@Res() res: ResponseExpress の Response
@Session()セッションを取得@Session() session: anyセッションデータ
@Ip()クライアント IP を取得@Ip() ip: stringリクエスト元 IP
@HostParam()サブドメインパラメータを取得@HostParam('account') account: stringサブドメイン情報

Custom Decorator パターン

パターン用途実装方法
基本的な作成リクエストから特定データを抽出createParamDecorator()
プロパティ指定オブジェクトの特定プロパティを取得data パラメータを活用
型安全な実装TypeScript の型チェックを有効化keyof と型定義を使用
Pipe 連携バリデーション・変換を追加Decorator の第 2 引数に Pipe
複数 Decorator 統合認証・認可などをまとめるapplyDecorators()
メタデータ設定Guard や Interceptor と連携SetMetadata()

この表を参考に、目的に応じた Decorator を素早く見つけられます。

背景

NestJS は Express や Fastify をベースにした、TypeScript で書かれた強力な Node.js フレームワークです。

Angular に影響を受けた設計思想を持ち、**Decorator(デコレーター)**を多用することで、宣言的かつ保守性の高いコード記述を実現します。Decorator を使うことで、ルーティング、パラメータ取得、バリデーション、認証といった横断的関心事を、シンプルな記法で表現できるのです。

以下の図は、NestJS における Decorator の基本的な役割を示しています。

mermaidflowchart TB
  req["HTTPリクエスト"] --> decorator["Decorator層<br/>(ルーティング・パラメータ取得)"]
  decorator --> guard["Guard<br/>(認証・認可)"]
  guard --> pipe["Pipe<br/>(バリデーション・変換)"]
  pipe --> controller["Controller<br/>(ビジネスロジック呼び出し)"]
  controller --> service["Service<br/>(ビジネスロジック)"]
  service --> res["HTTPレスポンス"]

この図からわかるように、Decorator は HTTP リクエストが Controller に到達する前段階で、ルーティングやパラメータ取得を担当します。

課題

NestJS の Decorator には多くの種類があり、それぞれ用途が異なります。

公式ドキュメントを読んでも、実際の開発現場で「どのパターンを使うべきか」が瞬時に判断できないことがあります。特に以下のような課題に直面しやすいです。

#課題具体例
1同じ目的でも複数の Decorator があり混乱する@Param()@Body() の使い分け
2Custom Decorator の作成方法がわかりにくい@User() のような独自 Decorator の実装
3パラメータの型定義が複雑@Param('id') で取得した値を数値型にする方法
4Decorator の組み合わせ順序に迷う@UseGuards()@UsePipes() の順番

これらの課題を解決するには、よく使うパターンを体系的に整理し、すぐに参照できる形にまとめることが有効です。

解決策

本記事では、NestJS の Decorator を以下の 3 つのカテゴリに分類し、それぞれの定型パターンを整理します。

#カテゴリ説明
1Controller Decoratorクラスやメソッドに適用し、ルーティングや HTTP メソッドを定義
2Param Decoratorメソッドの引数に適用し、リクエストデータを取得
3Custom Decorator独自のロジックをカプセル化した再利用可能な Decorator

以下の図は、これらの Decorator がどのように連携するかを示しています。

mermaidflowchart LR
  client["クライアント"] -->|"GET /users/:id"| controller["@Controller('users')"]
  controller --> method["@Get(':id')"]
  method --> param["@Param('id') id: string"]
  param --> custom["@User() user: UserEntity"]
  custom --> logic["ビジネスロジック"]
  logic --> response["レスポンス"]

この図のように、複数の Decorator を組み合わせることで、シンプルかつ読みやすいコードを実現できます。

それでは、各カテゴリの具体的なパターンを見ていきましょう。

具体例

Controller Decorator の定型パターン

Controller Decorator は、クラスレベルやメソッドレベルで適用し、ルーティングや HTTP メソッドを定義します。

ここでは、実務でよく使われる Controller Decorator のパターンを紹介します。

@Controller() - 基本的なルーティング

@Controller() デコレーターは、クラスをコントローラーとして定義し、ベースとなるルートパスを指定します。

typescriptimport { Controller } from '@nestjs/common';

// ベースパスを 'users' に設定
@Controller('users')
export class UsersController {
  // このクラス内のメソッドは /users/* でアクセス可能
}

このコードでは、UsersController クラス内のすべてのエンドポイントが ​/​users というパスの下に配置されます。

バージョン管理が必要な場合は、以下のように記述できます。

typescript// バージョン付きルーティング
@Controller('v1/users')
export class UsersV1Controller {
  // /v1/users/* でアクセス可能
}

@Get() / @Post() / @Put() / @Delete() - HTTP メソッド

各 HTTP メソッドに対応する Decorator を使って、エンドポイントを定義します。

typescriptimport {
  Controller,
  Get,
  Post,
  Put,
  Delete,
} from '@nestjs/common';

@Controller('users')
export class UsersController {
  // GET /users - 一覧取得
  @Get()
  findAll() {
    return 'This action returns all users';
  }
}

個別のリソースを取得する場合は、パスパラメータを指定します。

typescript  // GET /users/:id - 個別取得
  @Get(':id')
  findOne() {
    return 'This action returns a user';
  }

POST リクエストでリソースを作成する例です。

typescript  // POST /users - 新規作成
  @Post()
  create() {
    return 'This action adds a new user';
  }

PUT リクエストでリソースを更新します。

typescript  // PUT /users/:id - 更新
  @Put(':id')
  update() {
    return 'This action updates a user';
  }

DELETE リクエストでリソースを削除します。

typescript  // DELETE /users/:id - 削除
  @Delete(':id')
  remove() {
    return 'This action removes a user';
  }

このように、HTTP メソッドごとに専用の Decorator を使うことで、RESTful な API を簡潔に表現できます。

@HttpCode() - ステータスコード指定

デフォルトでは、POST は 201、その他は 200 を返しますが、任意のステータスコードを指定できます。

typescriptimport {
  Controller,
  Post,
  HttpCode,
  HttpStatus,
} from '@nestjs/common';

@Controller('users')
export class UsersController {
  // POST /users - ステータスコード 204 を返す
  @Post()
  @HttpCode(HttpStatus.NO_CONTENT)
  create() {
    // リソース作成後、ボディなしで 204 を返す
  }
}

HttpStatus 列挙型を使うことで、可読性が向上し、マジックナンバーを避けられます。

@Header() - レスポンスヘッダー設定

レスポンスにカスタムヘッダーを追加する場合は、@Header() を使います。

typescriptimport { Controller, Get, Header } from '@nestjs/common';

@Controller('users')
export class UsersController {
  // GET /users - カスタムヘッダー付きレスポンス
  @Get()
  @Header('Cache-Control', 'no-store')
  @Header('X-Custom-Header', 'CustomValue')
  findAll() {
    return 'Users data';
  }
}

これにより、キャッシュ制御やセキュリティヘッダーを簡単に設定できます。

@Redirect() - リダイレクト

特定の URL にリダイレクトする場合は、@Redirect() を使います。

typescriptimport { Controller, Get, Redirect } from '@nestjs/common';

@Controller('old-users')
export class OldUsersController {
  // GET /old-users を /users にリダイレクト
  @Get()
  @Redirect('/users', 301)
  redirectToUsers() {
    // 301 Moved Permanently でリダイレクト
  }
}

動的にリダイレクト先を変更する場合は、以下のように記述します。

typescript  @Get('docs')
  @Redirect()
  getDocs() {
    // 条件によってリダイレクト先を変更
    return { url: 'https://docs.nestjs.com', statusCode: 302 };
  }

Param Decorator の定型パターン

Param Decorator は、メソッドの引数に適用し、リクエストから必要なデータを取得します。

NestJS では、さまざまな Param Decorator が用意されており、それぞれ異なる用途に対応しています。

@Param() - パスパラメータ取得

URL のパスパラメータを取得するには、@Param() を使います。

typescriptimport { Controller, Get, Param } from '@nestjs/common';

@Controller('users')
export class UsersController {
  // GET /users/:id - id をパラメータとして取得
  @Get(':id')
  findOne(@Param('id') id: string) {
    return `User ID: ${id}`;
  }
}

このコードでは、​/​users​/​123 にアクセスすると、id"123" という文字列が格納されます。

複数のパラメータを取得する場合は、以下のようにします。

typescript  // GET /users/:userId/posts/:postId
  @Get(':userId/posts/:postId')
  findUserPost(
    @Param('userId') userId: string,
    @Param('postId') postId: string,
  ) {
    return `User: ${userId}, Post: ${postId}`;
  }

すべてのパラメータをオブジェクトとして取得することもできます。

typescript  @Get(':userId/posts/:postId')
  findUserPost(@Param() params: { userId: string; postId: string }) {
    return `User: ${params.userId}, Post: ${params.postId}`;
  }

@Query() - クエリパラメータ取得

URL のクエリパラメータを取得するには、@Query() を使います。

typescriptimport { Controller, Get, Query } from '@nestjs/common';

@Controller('users')
export class UsersController {
  // GET /users?page=1&limit=10
  @Get()
  findAll(
    @Query('page') page: string,
    @Query('limit') limit: string
  ) {
    return `Page: ${page}, Limit: ${limit}`;
  }
}

クエリパラメータは省略可能な場合が多いため、デフォルト値を設定すると便利です。

typescript  @Get()
  findAll(
    @Query('page') page: string = '1',
    @Query('limit') limit: string = '10',
  ) {
    return `Page: ${page}, Limit: ${limit}`;
  }

すべてのクエリパラメータをオブジェクトとして取得する例です。

typescript  @Get()
  findAll(@Query() query: { page?: string; limit?: string }) {
    return `Page: ${query.page || '1'}, Limit: ${query.limit || '10'}`;
  }

@Body() - リクエストボディ取得

POST や PUT リクエストのボディを取得するには、@Body() を使います。

typescriptimport { Controller, Post, Body } from '@nestjs/common';

@Controller('users')
export class UsersController {
  // POST /users - リクエストボディからユーザー情報を取得
  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return `Creating user: ${createUserDto.name}`;
  }
}

DTO(Data Transfer Object)を定義することで、型安全性とバリデーションを強化できます。

typescript// create-user.dto.ts
export class CreateUserDto {
  name: string;
  email: string;
  age?: number;
}

特定のプロパティだけを取得する場合は、以下のようにします。

typescript  @Post()
  create(@Body('name') name: string) {
    return `Creating user: ${name}`;
  }

@Headers() - リクエストヘッダー取得

リクエストヘッダーを取得するには、@Headers() を使います。

typescriptimport { Controller, Get, Headers } from '@nestjs/common';

@Controller('users')
export class UsersController {
  // GET /users - Authorization ヘッダーを取得
  @Get()
  findAll(@Headers('authorization') auth: string) {
    return `Authorization: ${auth}`;
  }
}

すべてのヘッダーをオブジェクトとして取得することもできます。

typescript  @Get()
  findAll(@Headers() headers: Record<string, string>) {
    return `User-Agent: ${headers['user-agent']}`;
  }

@Req() / @Res() - Express の Request / Response オブジェクト

Express の Request / Response オブジェクトに直接アクセスする場合は、@Req()@Res() を使います。

typescriptimport { Controller, Get, Req, Res } from '@nestjs/common';
import { Request, Response } from 'express';

@Controller('users')
export class UsersController {
  // Request オブジェクトに直接アクセス
  @Get()
  findAll(@Req() request: Request) {
    return `IP: ${request.ip}`;
  }
}

Response オブジェクトを使って、手動でレスポンスを返す例です。

typescript  @Get('custom')
  custom(@Res() response: Response) {
    // 手動でレスポンスを返す
    response.status(200).json({ message: 'Custom response' });
  }

注意点: @Res() を使うと、NestJS の自動レスポンス処理が無効になるため、必ず response.send()response.json() を呼ぶ必要があります。

@Session() - セッションデータ取得

セッションデータを取得するには、@Session() を使います(express-session が必要)。

typescriptimport { Controller, Get, Session } from '@nestjs/common';

@Controller('users')
export class UsersController {
  // GET /users/profile - セッションからユーザー情報を取得
  @Get('profile')
  getProfile(@Session() session: Record<string, any>) {
    return `User ID: ${session.userId}`;
  }
}

セッションにデータを保存する例です。

typescript  @Post('login')
  login(@Session() session: Record<string, any>, @Body('userId') userId: string) {
    session.userId = userId;
    return 'Logged in';
  }

@Ip() - クライアント IP アドレス取得

クライアントの IP アドレスを取得するには、@Ip() を使います。

typescriptimport { Controller, Get, Ip } from '@nestjs/common';

@Controller('users')
export class UsersController {
  // GET /users - クライアント IP を取得
  @Get()
  findAll(@Ip() ip: string) {
    return `Your IP: ${ip}`;
  }
}

このように、NestJS の Param Decorator を使うことで、リクエストから必要なデータを簡潔に取得できます。

Custom Decorator の定型パターン

NestJS では、独自の Decorator を作成することで、共通ロジックをカプセル化し、コードの再利用性を高められます。

ここでは、実務でよく使われる Custom Decorator のパターンを紹介します。

createParamDecorator - 基本的な作成方法

Custom Decorator を作成するには、createParamDecorator 関数を使います。

typescriptimport {
  createParamDecorator,
  ExecutionContext,
} from '@nestjs/common';

// @User() デコレーターを作成
export const User = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    // ExecutionContext から Request オブジェクトを取得
    const request = ctx.switchToHttp().getRequest();
    // request.user を返す(認証ミドルウェアで設定されている想定)
    return request.user;
  }
);

この Decorator を使うことで、コントローラーでユーザー情報を簡単に取得できます。

typescriptimport { Controller, Get } from '@nestjs/common';
import { User } from './user.decorator';

@Controller('profile')
export class ProfileController {
  @Get()
  getProfile(@User() user: any) {
    return `User: ${user.name}`;
  }
}

特定のプロパティだけを取得する Custom Decorator

ユーザーオブジェクト全体ではなく、特定のプロパティだけを取得する Decorator を作成できます。

typescriptimport {
  createParamDecorator,
  ExecutionContext,
} from '@nestjs/common';

// @User('id') のように使える Decorator
export const User = createParamDecorator(
  (data: string, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    const user = request.user;

    // data が指定されている場合は、そのプロパティを返す
    return data ? user?.[data] : user;
  }
);

この Decorator は、以下のように使います。

typescript@Controller('profile')
export class ProfileController {
  @Get()
  getProfile(@User('id') userId: string) {
    return `User ID: ${userId}`;
  }
}

引数を指定しない場合は、ユーザーオブジェクト全体を取得できます。

typescript  @Get('full')
  getFullProfile(@User() user: any) {
    return user;
  }

型安全な Custom Decorator

TypeScript の型定義を活用して、型安全な Custom Decorator を作成できます。

typescriptimport {
  createParamDecorator,
  ExecutionContext,
} from '@nestjs/common';

// ユーザーエンティティの型定義
export interface UserEntity {
  id: string;
  name: string;
  email: string;
  role: string;
}

// 型安全な @User() デコレーター
export const User = createParamDecorator(
  (
    data: keyof UserEntity | undefined,
    ctx: ExecutionContext
  ): UserEntity | any => {
    const request = ctx.switchToHttp().getRequest();
    const user = request.user as UserEntity;

    return data ? user?.[data] : user;
  }
);

この Decorator を使うことで、TypeScript の型チェックが効きます。

typescript@Controller('profile')
export class ProfileController {
  @Get()
  getProfile(@User() user: UserEntity) {
    // user は UserEntity 型
    return `User: ${user.name}`;
  }

  @Get('role')
  getRole(@User('role') role: string) {
    // role は string 型
    return `Role: ${role}`;
  }
}

Pipe と組み合わせた Custom Decorator

Custom Decorator と Pipe を組み合わせることで、バリデーションや変換処理を追加できます。

typescriptimport {
  Controller,
  Get,
  ParseIntPipe,
} from '@nestjs/common';
import { User } from './user.decorator';

@Controller('profile')
export class ProfileController {
  // @User('id') で取得した値を ParseIntPipe で数値に変換
  @Get('age')
  getAge(@User('age', ParseIntPipe) age: number) {
    return `Age: ${age}`;
  }
}

カスタム Pipe を作成して、より複雑な処理を行う例です。

typescriptimport {
  PipeTransform,
  Injectable,
  BadRequestException,
} from '@nestjs/common';

@Injectable()
export class ValidateUserPipe implements PipeTransform {
  transform(value: any) {
    if (!value || !value.id) {
      throw new BadRequestException('User not found');
    }
    return value;
  }
}

この Pipe を Custom Decorator と組み合わせます。

typescript@Controller('profile')
export class ProfileController {
  @Get()
  getProfile(@User(ValidateUserPipe) user: UserEntity) {
    return user;
  }
}

複数の Decorator を組み合わせた Custom Decorator

applyDecorators 関数を使うことで、複数の Decorator を 1 つにまとめられます。

typescriptimport {
  applyDecorators,
  UseGuards,
  SetMetadata,
} from '@nestjs/common';
import { AuthGuard } from './auth.guard';
import { RolesGuard } from './roles.guard';

// @Auth('admin') のように使える Decorator
export function Auth(...roles: string[]) {
  return applyDecorators(
    SetMetadata('roles', roles),
    UseGuards(AuthGuard, RolesGuard)
  );
}

この Decorator を使うことで、認証と認可を 1 行で設定できます。

typescript@Controller('admin')
export class AdminController {
  // 認証と admin ロールチェックを同時に適用
  @Get()
  @Auth('admin')
  adminOnly() {
    return 'Admin content';
  }
}

複数のロールを許可する例です。

typescript  @Get('moderator')
  @Auth('admin', 'moderator')
  moderatorContent() {
    return 'Moderator content';
  }

メタデータを設定する Custom Decorator

SetMetadata を使って、メタデータを設定する Custom Decorator を作成できます。

typescriptimport { SetMetadata } from '@nestjs/common';

// @Public() デコレーター(認証不要のエンドポイントをマーク)
export const Public = () => SetMetadata('isPublic', true);

この Decorator は、認証ガードでチェックできます。

typescriptimport {
  Injectable,
  CanActivate,
  ExecutionContext,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    // @Public() が付いている場合は認証スキップ
    const isPublic = this.reflector.get<boolean>(
      'isPublic',
      context.getHandler()
    );
    if (isPublic) {
      return true;
    }

    // 認証チェック処理
    const request = context.switchToHttp().getRequest();
    return !!request.user;
  }
}

コントローラーでの使用例です。

typescript@Controller('posts')
export class PostsController {
  // 認証が必要
  @Get()
  findAll() {
    return 'All posts';
  }

  // 認証不要
  @Get('public')
  @Public()
  publicPosts() {
    return 'Public posts';
  }
}

以下の図は、Custom Decorator がどのようにリクエスト処理フローに組み込まれるかを示しています。

mermaidsequenceDiagram
  participant Client as クライアント
  participant Decorator as Custom Decorator
  participant Guard as Guard
  participant Pipe as Pipe
  participant Controller as Controller
  participant Service as Service

  Client->>Decorator: HTTPリクエスト
  Decorator->>Guard: メタデータ設定・パラメータ抽出
  Guard->>Pipe: 認証・認可チェック
  Pipe->>Controller: バリデーション・変換
  Controller->>Service: ビジネスロジック呼び出し
  Service-->>Controller: 処理結果
  Controller-->>Client: HTTPレスポンス

この図から、Custom Decorator がリクエスト処理の最初の段階でメタデータ設定やパラメータ抽出を行い、後続の Guard や Pipe に情報を渡していることがわかります。

図で理解できる要点

  • Custom Decorator は、リクエスト処理の入口で動作し、後続の処理に必要な情報を準備します
  • createParamDecorator で作成した Decorator は、ExecutionContext からリクエストデータを取得できます
  • applyDecorators を使うことで、複数の Decorator を組み合わせて再利用性を高められます
  • メタデータを設定することで、Guard や Interceptor と連携した柔軟な処理が可能になります

まとめ

この記事では、NestJS の Decorator を 3 つのカテゴリに分けて、定型パターンを体系的にまとめました。

Controller Decorator は、ルーティングや HTTP メソッドを宣言的に定義し、RESTful API の構造を明確にします。Param Decorator は、リクエストから必要なデータを簡潔に取得し、型安全性を保ちながら開発効率を向上させるでしょう。

Custom Decorator を活用することで、共通ロジックをカプセル化し、コードの再利用性と保守性が大きく向上します。特に、認証・認可・バリデーションといった横断的関心事を Decorator として定義することで、コントローラーのコードがシンプルになり、ビジネスロジックに集中できるようになります。

実際の開発では、この記事で紹介したパターンをベースに、プロジェクトの要件に合わせてカスタマイズしていくと良いでしょう。NestJS の Decorator を使いこなすことで、保守性が高く、拡張しやすい API を構築できます。

この速見表が、日々の NestJS 開発の参考になれば幸いです。

関連リンク