😺

Chapter1: NestJSを使ってみた 文字列を返すシンプルなAPI実装

2022/03/08に公開

Prev Chapter

これはなに

このチャプターでは、以下の2つのAPIを実装します。

  • GET /users
  • POST /users
    今回は、実装したルートにアクセスすると文字列が返却されるシンプルなAPIを実装し、各クラスや機能を確認していきます。

今回実装するクラスは以下です。

  • UserModule
  • UserController
  • UserService

githubのリポジトリも公開しているので必要であれば、こちらも参照してみてください。
https://github.com/rinonkia/nest-tutorial/tree/feat/initial_user_module

前Chapterでも記載しましたが、公式ドキュメントのこちらのページの後にすすめてもらえるとスムーズだと思います。
https://docs.nestjs.com/first-steps

NestJSのModuleについて

NestJSはモジュラアーキテクチャを採用しています。モジュラアーキテクチャとは構成要素ごとにモジュール化した部品を作成し、それらを結合し機能を実装します。モジュールごとに独立して実装を行うので各モジュール同士は疎結合な状態を保つことが出来、実装の柔軟性やテスト容易性を実現できます。

NestJSのアプリは基本的に1つのルートモジュール(AppModule)を持っており、ルートモジュールに実装した各モジュールimportさせて、アプリの機能を追加していきます。

Moduleの関係サンプル

コードサンプル

./src/app.moduel.ts
import { Module } from '@nestjs/common';
import { UserModule } from './modules/user/user.module';
import { PostModule } from './modules/post/post.module';
import { MessageModule } from './modules/message/message.module';

@Module({
  imports: [UserModule, PostModule, MessageModule],
})
export class AppModule {}

実装

ここから実装に入ります。

UserController

NestJSは一般的なフレームワークと違い、ルーティングを別ファイルで定義するのではなくコントローラで定義します。コントローラを見るだけで、どのルーティングからコントローラのメソッドが呼び出されているかが確認できます。NestJSはルーティングとコントローラの分離による煩雑さをこのような手法で解決しています。

./src/moduels/user.controller.ts
import { Controller, Get } from '@nestjs/common';
import { UserService } from './user.service';

@Controller('users')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get()
  findAll() {
    return this.userService.findAll();
  }
  
  @Post()
  create(() {
    return this.userService.create();
  }
}

@Controller()デコレータでパスのプレフィックスをusersを設定します。
Controllerの各メソッドに@Get(), @Post()デコレータを修飾することで以下のルーティングが設定できます。

パス メソッド Controllerメソッド
/users GET findAll()
/users POST create()

UsersController@Get('hogehoge')で修飾したメソッドを実装すれば、GET /users/hogehogeルートを設定することも可能です。

constructorUserServiceを生成し、各メソッドで利用します。

UserService

UserControllerで利用する`UserServiceを実装します。

./src/modules/user.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class UserService {
  findAll() {
    return 'findAll users';
  }
  
  create() {
    return 'create user';
  }
}

NestJSにはDependency Injection(依存注入)というデザインパターンを採用しています。先に実装したUserControllerもコンストラクタでUserServiceを依存注入します。
NestJSでは注入するクラスを Providerと呼び、@Injectableデコレータを修飾することで他のクラスに依存注入が出来るようになります。

UserModule

Moduleクラスでは必要な依存関係を整理し、カプセル化を行います。
実装したUserControllerUserServiceに依存しています。

@Module()デコレータを利用してUserModuleを実装します。

./src/user/user.module.ts
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';

@Module({
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}

@ModuleデコレータのcontrollersUserControllerprovidersUserServiceをセットします。

AppModule

UserModuleをルートモジュールであるAppModuleにimportします。
AppController, AppServiceは利用しないので消してしまいます。

./app.module.ts
import { Module } from '@nestjs/common';
import { UserModule } from './modules/user/user.module';

@Module({
  imports: [UserModule],
})
export class AppModule {}

以下のファイルも不要なので削除してしまいます。

  • app.controller.spec.ts
  • app.controller.ts
  • app.service.ts

最終的なディレクトリ構成は以下のようになります。

src
├── app.module.ts
├── main.ts
└── modules
    └── user
        ├── user.controller.spec.ts
        ├── user.module.ts
        ├── user.service.spec.ts
        ├── user.service.ts
        └── users.controller.ts

動作確認

実装が終わったので動作の確認します。

$ npm run start:dev

curlでAPIを叩いてみて以下のレスポンスが帰ってきたら成功です。

$ curl http://localhost:3000/users
findAll users%
$ curl -X POST http://localhost:3000/users 
create user%

試しにUserModuleのProviderのUserServiceを削除し、npm run start:devを行ってみると
providersに必要なクラス(UserService)がないので、依存関係の解決が出来ずに以下のようなエラーが出ます。

ためしに削除してみるとNestJSのModuleの役割が確認できるかと思います。

Error: Nest can't resolve dependencies of the UserController (?). Please make sure that the argument UserService at index [0] is available in the UserModule context.

おわり

今回修正した内容です。
https://github.com/rinonkia/nest-tutorial/commit/be5c85793316576448a819f7b0b46d89038efbbf

次のChapterはPrismaのインストール/設定を行ない、マイグレーションを行います。

https://zenn.dev/gatapon/articles/906c13176d8537

Discussion