9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【NestJS】 Controllerメソッドにasync/awaitつける?つけない?

Last updated at Posted at 2025-07-13

はじめに

NestJSを書いていて気になったことを調べた時のメモをまとめたものです。

動作環境

項目 バージョン
Node.js v20.12.2
TypeScript v5.8.2
NestJS v10.4.15
Yarn v1.22.19

基本原則

NestJSは Promiseを自動的に解決 してからHTTPレスポンスを送信するため、必ずしもasync/awaitは必要ありません。

使い分けの指針

※ わかりやすくするために、「本来Controllerでする処理じゃないだろ...」的な処理も書いています。

✅ async/await を使うべきケース

1. 複数の非同期処理を順番に実行する場合

@Post()
async createUser(@Body() dto: CreateUserDto) {
  // 順次実行が必要
  await this.userService.validateUser(dto);
  const user = await this.userService.create(dto);
  await this.notificationService.sendWelcomeEmail(user.email);
  return user;
}

2. try-catch でエラーハンドリングが必要な場合

@Get(':id')
async findUser(@Param('id') id: number) {
  try {
    const user = await this.userService.findOne(id);
    return user;
  } catch (error) {
    // 特定のエラーハンドリングが必要
    if (error instanceof UserNotFoundError) {
      throw new NotFoundException('ユーザーが見つかりません');
    }
    throw error;
  }
}

3. 条件分岐で非同期処理の結果を使う場合

@Put(':id')
async updateUser(@Param('id') id: number, @Body() dto: UpdateUserDto) {
  const existingUser = await this.userService.findOne(id);

  if (existingUser.role === 'admin') {
    // 管理者の場合は特別な処理
    return await this.userService.updateAdmin(id, dto);
  }

  return await this.userService.update(id, dto);
}

4. 非同期処理の結果を加工してから返す場合

@Get()
async getUsers(@Query() query: UserQueryDto) {
  const users = await this.userService.findAll(query);

  // 結果を加工
  return {
    users: users.map(user => ({
      ...user,
      fullName: `${user.firstName} ${user.lastName}`
    })),
    totalCount: users.length
  };
}

❌ async/await を使わなくていいケース

1. 単純にPromiseを返すだけの場合

@Get(':id')
findUser(@Param('id') id: number): Promise<User> {
  // そのままPromiseを返す(NestJSが自動解決)
  return this.userService.findOne(id);
}

@Get()
getUsers(@Query() query: UserQueryDto): Promise<User[]> {
  return this.userService.findAll(query);
}

2. マイクロサービス通信の単純な中継

@Get(':userId')
findUser(@Param('userId', ParseIntPipe) id: number): Promise<User> {
  // マイクロサービスへの単純な転送
  return this.microServiceClient.send<User>({ cmd: 'findUser' }, { id });
}

実践的なコード例

✅ async/await を使っている例(適切)

@Get()
async findAll(
  @Query() queryParams: QueryDto,
  @Req() req: Request,
): Promise<{ users: User[]; totalCount: number }> {
  const res = await this.serviceClient.send<{ users: User[]; totalCount: number }>(
    { cmd: "findAllUsers" },
    { queryParams, user: req.user },
  );
  // 解決された値をログ出力してからレスポンス
  this.logger.log(`Found ${res.totalCount} users`);
  return res;
}

❌ async/await を使わない例(適切)

@Get(':userId')
findOne(@Param('userId', ParseIntPipe) id: number): Promise<User> {
  // 単純な転送なのでasync/awaitは不要
  return this.serviceClient.send<User>({ cmd: 'findUser' }, { id });
}

まとめ

ケース async/await 理由
単純なPromise返却 ❌ 不要 NestJSが自動解決
複数の非同期処理 ✅ 必要 順次実行制御のため
try-catch が必要 ✅ 必要 エラーハンドリングのため
条件分岐で非同期結果を使用 ✅ 必要 値の評価が必要
結果の加工が必要 ✅ 必要 解決された値の操作
デバッグログ出力 ✅ 推奨 実際の値をログ出力

原則: 単純にPromiseを返すだけなら async/await は不要。処理が必要なら async/await を使用。

参考文献

9
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?