あなたはすぐに解決できる？NestJsのコントローラ実装で2時間詰まった話

### はじめに
今回の記事では、 **「実際に実務で遭遇したエラー」と「その解決法」** をまとめました！原因さえわかれば文字通り**一瞬で**解決できる問題です！皆さんもエラー原因について考えてみてください！
また、エラー解決方法がわかった人は**それを人に説明できるか**考えてみてください！

※ コードについては少し修正等を加えています。

### 該当のコード (下記の実装ではエラーが起きます)

```typescript
@Controller('sample')
export class SampleController {
  constructor(private readonly sampleService: SampleService) {}

  @Get()
  @ApiOperation({ summary: '一覧の取得' })
  @ApiOkEnvelopedResponse({
    type: SampleExEntity,
    isArray: true,
  })
  async findAllList(
    @User() user: AuthedUser,
    @Query() dto: SearchSampleListDto,
    @Query() pageOption: PageOptionDto,
  ) {
    const [samples, count] = await this.samplesService.findAll(
      user,
      dto,
      pageOption,
    );
    return [
      samples.map(mapToSampleListExEntity),
      PageEntity.new(count, pageOption),
    ];
  }

  @Post()
  @ApiOperation({ summary: '新規作成' })
  @ApiOkEnvelopedResponse({
    type: SampleExEntity,
  })
  async create(
    @User() user: AuthedUser,
    @Body() dto: CreateSampleDto,
  ): Promise<SampleExEntity> {
    const sample = await this.samplesService.create(user, dto);
    return mapToSampleListExEntity(sample);
  }

  @Put(`:id`)
  @ApiOperation({ summary: '編集' })
  @ApiOkEnvelopedResponse({
    type: SampleExEntity,
  })
  async update(
    @Param('id', ParseIntPipe) sampleId: number,
    @User() user: AuthedUser,
    @Body() dto: UpdateSampleDto,
  ): Promise<SampleExEntity> {
    const sample = await this.samplesService.update(
      sampleId,
      user,
      dto,
    );
    return mapToSampleListExEntity(sample);
  }

  @Put('assign')
  @ApiOperation({ summary: '一括更新' })
  @ApiOkEnvelopedResponse({
    type: SampleExEntity,
    isArray: true,
  })
  async assignSamplesToTests(
    @User() user: AuthedUser,
    @Body() dto: AssignSamplesToTestsDto,
  ): Promise<SampleExEntity[]> {
    const samples = await this.samplesService.assignSamplesToTests(
      user,
      dto,
    );
    return samples.map(mapToSampleListExEntity);
  }
}
```

<details>
<summary>解決のヒントを見る</summary>

- APIを実行した際のエラーコードは「400 Bad Request」
- `assignSamplesToTests`のエンドポイントを`PUT /sample`にした場合はOK、`PUT /sample/assign`にした場合はエラー発生！！

</details>

### 解決までに試したこと

<details>
<summary>試したこと(1)</summary>

```
やったこと：該当の処理でログを出力する
```

```
結果：出力されなかった(処理が呼ばれてない？)
```

```typescript
@Put('assign')
  @ApiOperation({ summary: '一括更新' })
  @ApiOkEnvelopedResponse({
    type: SampleExEntity,
    isArray: true,
  })
  async assignSamplesToTests(
    @User() user: AuthedUser,
    @Body() dto: AssignSamplesToTestsDto,
  ): Promise<SampleExEntity[]> {
    console.log('APIが呼ばれました！！') // ⇦ これが呼ばれるか検証
    const samples = await this.samplesService.assignSamplesToTests(
      user,
      dto,
    );
    return samples.map(mapToSampleListExEntity);
  }
```

</details>

<details>
<summary>試したこと(2)</summary>

```
やったこと：エンドポイントを色々と変えて試してみた！
```

```
結果：変わらずエラー発生・・・
```

```typescript
@Put('test') // ⇦ ここを色々と変更してみた
  @ApiOperation({ summary: '一括更新' })
  @ApiOkEnvelopedResponse({
    type: SampleExEntity,
    isArray: true,
  })
  async assignSamplesToTests(
    @User() user: AuthedUser,
    @Body() dto: AssignSamplesToTestsDto,
  ): Promise<SampleExEntity[]> {
    const samples = await this.samplesService.assignSamplesToTests(
      user,
      dto,
    );
    return samples.map(mapToSampleListExEntity);
  }
```

</details>

<details>
<summary>試したこと(3)</summary>

```
やったこと：エラーコードを確認 
```

```
結果：400 (エンドポイントの設定方法は合ってそう？？)
```

</details>

<details>
<summary>試したこと(4)</summary>

```
やったこと：エラーメッセージの確認
```

```
結果：「OOは必須です！」 (あれ？DTOが違う？)
```

</details>

<details>
<summary>試したこと(5)</summary>

```
やったこと：エラーで弾いていそうなDTOを考察
```

```
結果：呼ばれているAPIが違う？
```

</details>

<details>
<summary>試したこと(6)</summary>

```
やったこと：異なるAPIが呼ばれている理由を調べる & 修正対応
```

```
結果：無事解決！！
```

</details>

### 解決済み(エラー解消済み)のコード

<details>
<summary>クリックで表示されます！！</summary>

```typescript
@Controller('sample')
export class SampleController {
  constructor(private readonly sampleService: SampleService) {}

  @Get()
  @ApiOperation({ summary: '一覧の取得' })
  @ApiOkEnvelopedResponse({
    type: SampleExEntity,
    isArray: true,
  })
  async findAllList(
    @User() user: AuthedUser,
    @Query() dto: SearchSampleListDto,
    @Query() pageOption: PageOptionDto,
  ) {
    const [samples, count] = await this.samplesService.findAll(
      user,
      dto,
      pageOption,
    );
    return [
      samples.map(mapToSampleListExEntity),
      PageEntity.new(count, pageOption),
    ];
  }

  @Post()
  @ApiOperation({ summary: '新規作成' })
  @ApiOkEnvelopedResponse({
    type: SampleExEntity,
  })
  async create(
    @User() user: AuthedUser,
    @Body() dto: CreateSampleDto,
  ): Promise<SampleExEntity> {
    const sample = await this.samplesService.create(user, dto);
    return mapToSampleListExEntity(sample);
  }

  @Put('assign')
  @ApiOperation({ summary: '一括更新' })
  @ApiOkEnvelopedResponse({
    type: SampleExEntity,
    isArray: true,
  })
  async assignSamplesToTests(
    @User() user: AuthedUser,
    @Body() dto: AssignSamplesToTestsDto,
  ): Promise<SampleExEntity[]> {
    const samples = await this.samplesService.assignSamplesToTests(
      user,
      dto,
    );
    return samples.map(mapToSampleListExEntity);
  }

  @Put(`:id`)
  @ApiOperation({ summary: '編集' })
  @ApiOkEnvelopedResponse({
    type: SampleExEntity,
  })
  async update(
    @Param('id', ParseIntPipe) sampleId: number,
    @User() user: AuthedUser,
    @Body() dto: UpdateSampleDto,
  ): Promise<SampleExEntity> {
    const sample = await this.samplesService.update(
      sampleId,
      user,
      dto,
    );
    return mapToSampleListExEntity(sample);
  }
}
```

</details>

### 今回の実装での反省
Q どうすればエラー解決を爆速でできたか？

⇨ **結論：「エラーコード」&「エラーメッセージ」から何が起きているのかを考える！**

色々と手を動かしながら試行錯誤してしまいましたが、最初からレスポンスのエラーメッセージを確認して、「なぜ他のAPIが呼ばれているのか？」を考えれば、解決までに5分もかからなかったと思います。
単純に2時間かかる作業を5分でできるなら、作業効率が24倍 ⇨ 時間単価が24倍になると考えると大きな違いですよね。(空いた時間を他の作業や学習に充てられるため)

### 最後に
いかがだったでしょうか？皆さんは「エラーの解決」および「エラー原因の説明」はできましたか？
なぜ上記のコードで解決できるのかわからなかった人は、ぜひ調べてみてください！
今後も実務で詰まった点や解決方法などをまとめていきたいと思っています。

## 株式会社シンシア
<a href="https://corp.xincere.jp/" target="_blank" rel="noopener noreferrer">株式会社xincere</a>では、実務未経験のエンジニアの方や学生エンジニアインターンを採用し一緒に働いています。
※ シンシアにおける働き方の様子はこちら

https://www.wantedly.com/companies/xincere-inc/stories

シンシアでは、年間100人程度の実務未経験の方が応募し技術面接を受けます。
その経験を通し、実務未経験者の方にぜひ身につけて欲しい技術力（文法）をここでは紹介していきます。