NestJSに入門してみる

タイトル通り、NestJSに入門してみます。

最近Nuxtのアップデート作業も落ち着いてきて次はTypeScript導入、というタイミングでバックエンドもTypeScriptで書けたら面白いかもと思ったのがきっかけです。

NestJSとは

A progressive Node.js framework for building efficient, reliable and scalable server-side applications.

とあるように効率的かつ信頼性の高いスケーラブルなサーバーサイドアプリケーション構築のためのNode.jsフレームワークです(直訳)。

Node.jsのフレームワークといえばExpressが長らくその地位を占めていたように思いますが、最近ではNestJSの名前も以前より聞くようになってきました。

GithubのStartも5万7千件、対するExpressは6万1千件なのでだいぶ近づいています。

と思ったら内部的にExpressを使っているみたいです。

公式ドキュメントはまだ日本語対応していないようです。

はじめてみる

というわけで何はともあれ始めてみます。

公式ドキュメントに沿って進めます。

まずはNest CLIをインストールします。

$ npm i -g @nestjs/cli

余談ですが-gでグローバルインストールしようとしたらこんな警告が出ました。

npm WARN config global `--global`, `--local` are deprecated. Use `--location=global` instead.

いつの間にかdeprecatedになっていたようです。

とりあえずインストールできたか確認してみます。

$ nest -v
zsh: command not found: nest

いきなりつまずきました。

色々調べた結果、.zshrcにaliasを設定する必要がありました。

npm root --location=global

でパスを確認して以下を追記します。

alias nest="{上記で確認したパス}/@nestjs/cli/bin/nest.js"

追記したら

$ source ~/.zshrc

で反映させると、

$ nest -v
9.5.0

無事表示されました。

ではNestJSのプロジェクトを作成します。

$ nest new nestjs_sample

パッケージマネージャーをnpm / yarn / pnpmの中から選んでしばらく待つとセットアップが完了します。

$ nest start

を実行するとポート3000番で起動します。

とりあえずHello Worldできました。

NestJSの構成

自動で生成されたファイルを見てみます。

srcディレクトリの下に生成されたファイルがアプリケーションコードです。

NestJSはController / Provider / Moduleの3つが主な要素となります。

Controller

ControllerMVCにおけるControllerと同じくリクエストを受けてレスポンスを返す処理を担当します。

加えてNestJSではルーティングもControllerで設定します。app.controller.tsを見てみましょう。

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}

@Controller()というデコレータでControllerを定義しています。

@Controller()デコレータはprefixを引数に取ることができ、それによってルートをグルーピングできます。

例えば@Controller('cats')とするとlocalhost:3000/catsに対応します。

同様に@Get()デコレータもパスパラメータを引数に取ることができます。

ID等、動的なパラメータを取りたいときは@Get(':id')のようにします。

また、お察しの通り@Get()以外に@Post()@Put()はそれぞれPOST / PUTに対応しています。

Provider

続いてapp.service.tsを見てみます。

import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

Providerはサービスやリポジトリ、ファクトリ等、他のクラスに依存関係として注入されるものを指します。

LaravelでいうところのServiceProviderで管理されるクラスに相当する、という認識です。

@Injectable()という新しいデコレータが登場しました。

これはDIコンテナで管理できる(=依存関係として注入できる)クラスであることを示すためのメタデータを付加します。

Module

最後にapp.module.tsを見てみます。

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

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

Controllerと同様に@Module()デコレータでModuleであることを宣言します。

Each application has at least one module, a root module. The root module is the starting point Nest uses to build the application graph - the internal data structure Nest uses to resolve module and provider relationships and dependencies. While very small applications may theoretically have just the root module, this is not the typical case. We want to emphasize that modules are strongly recommended as an effective way to organize your components. Thus, for most applications, the resulting architecture will employ multiple modules, each encapsulating a closely related set of capabilities.

とあるように、Moduleという名前の通り各機能ごとに必要なControllerやService、その他のクラスをまとめ、カプセル化されたModule(公式ドキュメントではFeature modulesと呼んでいます)をアプリケーションのRoot Module(上記のapp.module.tsがそれですね)でimportするのが基本形です。

公式ドキュメントからコードを引用すると以下のようなイメージです。

// /src/cats/cats.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class CatsModule {}
// /src/app.module.ts
import { Module } from '@nestjs/common';
import { CatsModule } from './cats/cats.module';

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

main.ts

以上Controller / Provider / Moduleを説明してきましたが、アプリケーションのエントリーポイントとなるのがこのmain.tsです。

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();

NestFactory.create(AppModule)でNestJSのインスタンスを生成し、ポート3000番でlistenしています。

まとめ

以上、NestJSの基本をざっくりまとめてみました。

次回以降、DIやORM等について少しずつ進めていければと思います。