【NestJS】シンプルなREST APIを実装する

NestJS

NestJS CLIを使ってプロジェクトの雛形を作成する

次のnest newコマンドで今回作るアプリケーションのプロジェクトを作成します。

$ nest new プロジェクト名

今回のプロジェクト名は「nestjs_todo_list_rest_api」なので、実行するコマンドはこのようになります。

$ nest new nestjs_todo_list_rest_api

また、nest newを実行したらnodeのパッケージマネージャーをどれにするか聞かれると思うので、npmを選択してEnterをクリックします。

? Which package manager would you ❤️  to use? (Use arrow keys)
❯ npm
  yarn
  pnpm

NestJS デフォルトの構成を確認する

デフォルトのプロジェクト構成を確認すると、プロジェクトのルート階層には、NestJSのツールに必要な設定ファイルがあります。

設定フィアルは好みでカスタマイズできますが、デフォルトの構成が良いと思います。

コントローラーやモデルといったソースコードはsrcフォルダに入ることになり、デフォルトでは、

  • app.controller.spec.ts
  • app.controller.ts
  • app.module.ts
  • app.service.ts
  • main.ts

が作成されています。

package.jsonのデフォルト内容を確認する

package.jsonは、npmによって使用されるファイルであり、以下の役割を持ちます。

  • 依存関係を管理する
  • スクリプトコマンドを提供する
  • 基本的なプロジェクト定義を提供する

このファイルの中で、設定されたスクリプトは、npm run {スクリプト名}で実行することができます。

よく使うスクリプトコマンドを4つ紹介します。

  • npm run build:TypeScriptのコードをJavaScriptのコードにトランスパイルする
  • npm run format:prettierを活用して、コードを統一的なスタイルガイドにフォーマットする
  • npm run start:NestJSのWebサーバを起動する
  • npm run start:dev:npm run startコマンドと似てるが、–watchフラグを使用してファイルウォッチャーを有効にし、コードが変更されたら自動的にコードを再構築してWebサーバーを再起動する

NestJSのサーバーを起動してみる

それではNestJSアプリケーションを起動してみましょう。

$ npm run start:dev

[16:38:17] File change detected. Starting incremental compilation...
[16:38:17] Found 0 errors. Watching for file changes.

[Nest] 31721  - 2022/01/08 16:38:17     LOG [NestFactory] Starting Nest application...
[Nest] 31721  - 2022/01/08 16:38:17     LOG [InstanceLoader] AppModule dependencies initialized +21ms
[Nest] 31721  - 2022/01/08 16:38:17     LOG [RoutesResolver] AppController {/}: +4ms
[Nest] 31721  - 2022/01/08 16:38:17     LOG [RouterExplorer] Mapped {/, GET} route +2ms
[Nest] 31721  - 2022/01/08 16:38:17     LOG [NestApplication] Nest application successfully started +1ms

上記のようにログで「Nest application successfully started」となれば起動できてます。

ブラウザからlocalhost:3000にアクセスすると画面上に「Hello World!」と表示されます。

todoアプリケーションを実装する

todoディレクトリを作成し、必要となるファイルを揃えます。

といっても、NestJSのCLIはコマンドを実行するだけでcontrollerやmoduleのファイルを作成することができます。

$ nest generate module todo
$ nest generate controller todo
$ nest generate service todo
$ nest generate interface todo

上記のコマンドを実行すると、todoフォルダ配下に5つのファイルが作成され、モジュールの依存関係も自動で上書きしてくれます。

todo.interface.tsだけはsrcフォルダ配下に作られるので手動でtodoフォルダ内に移動させてあげます。

Todo インターフェースの作成

todo/todo.interface.tsファイルの内部で、TodoのJSONオブジェクトの構造を定義します。

インターフェースはクラスとは異なり「抽象的」であり、JavaScriptのコードは出力されず、純粋にTypeScriptコンパイラとintellisenseツールのために使用されます。

インターフェースを作成するにはTypeScriptのinterfaceキーワードを使いますが、今回は既にCLIで作成されてます。

export interface Todo {}

今回作成するTodoクラスは、

  • id:オプショナルな値(新しいTodoのリクエストに、idが自動で割り当てられる)
  • label:文字列
  • complete:真偽値(Todoが完了したかどうか)

という3つのプロパティを持つので、これらを以下のようにインターフェースに追加します。

export interface Todo {
  id?: number;
  label: string;
  complete: boolean;
}

これでTodoの構造を定義することができました。

また、先頭にexportキーワードがついているため、他のTypeScriptファイルでもこのインターフェースを使用することが可能です。

TodoServiceの作成

規定のNestJSアーキテクチャ・パターンに従い、Todoを管理するためのデータ・ロジックを扱うTodoServiceクラスを作成します。

todo.service.tsファイル内で、エクスポートされたTodoServiceクラスを定義し、@Injectableデコレータを使用して装飾します。

本記事では、ストレージ機構としてインメモリ配列を使用します。
永続的にデータが保存される訳ではないので、開発サーバーを再起動するたびに消去されますが、シンプルな仕組みとしてDBの代わりに使います。

この配列を作成するために、クラス変数storageを定義してインスタンス化します。

すると、todo.service.tsクラスは次のようになります。

import { Todo } from './todo.interface';
import { Injectable } from '@nestjs/common';

@Injectable()
export class TodoService {
  private storage: Todo[] = [];
}

NestJSのプロバイダはデフォルトでシングルトンであり、TodoServiceをインジェクションすると、同じインメモリ配列を共有します。

TodoControllerの作成

次に、HTTPリクエストを処理するためのControllerを作っていきます。

NestJSのCLIコマンドによって、todo.controller.tsは既に作成され、@Controllerデコレータで装飾されたTodoControllerクラスが用意されてます。

前項で作成したTodoServiceを活用するために、コンストラクタ注入によるNestJSの依存性注入を利用します。

TypeScriptのコンストラクタを利用することで、プロバイダ(TodoService)を、privateで読み取り専用のインスタンスプロパティとして簡単に設定することができます。

import { TodoService } from './todo.service';
import { Controller } from '@nestjs/common';

@Controller('todo')
export class TodoController {
  constructor(private readonly todoService: TodoService) {}
}

これで、this.todoServiceを介して、コントローラメソッドでTodoServiceを使用できるようになります。

次にモジュールを検証するため、空の配列を返すGETルートを1つ設定します。 (この際、Getをimportすることに注意してください)

import { Todo } from './todo.interface';
import { TodoService } from './todo.service';
import { Controller, Get } from '@nestjs/common';

@Controller('todo')
export class TodoController {
  constructor(private readonly todoService: TodoService) {}

  @Get()
  findAll(): Todo[] {
    return [];
  }
}

ルートは、HTTPメソッドのデコレーターを使用して定めます。

NestJSで使用するルートは、

  • @Get
  • @Post
  • @Put
  • @Patch
  • @Delete

の5つです。

TodoModuleの作成

ControllerクラスとProviderクラス(Serviceクラス)が作成できたので、次にこれらをNestJSアプリケーションに登録するためのModuleクラスを作成し、@Moduleデコレータで装飾する必要があります。

とはいえ、NestJSのCLIコマンドを実行した時、controllers配列にTodoController、providers配列にTodoServiceが登録されているはずです。

import { Module } from '@nestjs/common';
import { TodoController } from './todo.controller';
import { TodoService } from './todo.service';

@Module({
  controllers: [TodoController],
  providers: [TodoService],
})
export class TodoModule {}

TodoModuleをAppModuleにインポートする

HTTPリクエストからデータの保存までの機能を含んだモジュールが完成しました。

この時、エントリポイントであるmain.ts はAppModuleからINestApplicationを作成するので、TodoModuleへの参照がなければTodoModuleのロジックは利用できません。

ですがNestJSのCLIはコマンドでモジュールを作成した時点で、app.module.tsにモジュールへの参照を登録しているので、以下のコードになっているはずです。

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TodoModule } from './todo/todo.module';

@Module({
  imports: [TodoModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

@Moduleデコレータで、TodoModuleをimports配列に追加することで、TodoModuleの機能を使用することが可能になります。

TodoControllerにリクエストを送ってみる

npm run start:devで再度サーバーを起動させます。

コンソール出力に、NestJSがTodoModuleを読み込み、TodoControllerのルートを作成しているのが確認できるはずです。

そしてサーバーを起動したままPostmanを開き、GETメソッドで http://localhost:3000/todo にリクエストを行います。

TodoControllerはfindAll()メソッドを実行し、空の配列のJSONをレスポンスとして返します。

以上で、HTTPリクエストをTodoControllerに送れるようになり、コントローラーやサービスに CRUD処理を追加する準備が整いました。

タイトルとURLをコピーしました