NestJs入門 基本構造編

9155 ワード

概要

この記事でできること

  • NestJsの基本構造の全体像を理解
    • NestJsの公式のOVERVIEWでいう、ControllersProvidersModulesPipes
    • RESTful APIで投稿作成(POST)、投稿取得(GET)
      • ハンズオンで、ユーザーの作成を行う

対象読者

  • これからNestJsを学ぼうと思っている人
  • 仕事でNestJsを使うことになって、構造を理解したい人

使用技術

  • nest v8.2.5

手順

1. NestJs導入
2. main.tsとは?
3. Moduleとは?
4. Controllerとは?
5. Providerとは?
6. Usersフォルダを作成してみる
7. ユーザー作成APIを作ってみる
8. 作成したユーザーを取得してみる
9. ユーザー作成にバリデーションを設定する

1. NestJs導入

NestJsとは?

NestJsの公式サイトには以下のように説明されています。

  • 効率的でスケーラブルなNode.jsサーバー側アプリケーションを構築するためのフレームワーク。
  • プログレッシブJavaScriptを使用し、 TypeScriptで構築され、完全にサポートされ(ただし、開発者は純粋なJavaScriptでコーディングできます)、OOP(オブジェクト指向プログラミング)、FP(関数型プログラミング)、およびFRP(関数型リアクティブプログラミング)の要素を組み合わせることができる。
  • 内部的には、NestはExpress(デフォルト)などの堅牢なHTTPサーバーフレームワークを利用し、オプションでFastifyを使用するように構成することもできます。

簡単に要約すると、

  1. コードを保守しやすいサーバー側のフレームワーク。
  2. デフォルトでTypeScriptで構築されている
  3. Expressとどっちを使うかではなく、Expressをラップしたフレームワーク。

NestJs CLIをインストール

nodeはインストールしていると仮定して勧めていきます。
インストールをしていない方がいたらNodeJsの公式サイトからダウンロードしておいてください。

NestJsには独自のNestJsのCLIコマンドが使えるので、インストールします。

$ npm install -g @nestjs/cli

このコマンドを実行して成功するとnestコマンドを利用できるようになります。

新規NestJsプロジェクトを作成

新規のプロジェクトを作成するにはnest newコマンドを実行します。
今回はnest-lessonというプロジェクトを作成することにします。
nest-lessonの部分には任意の名前を指定できます。

$ nest new nest-lesson

実行すると、npmを使うかyarnを使うか聞かれるので今回はnpmを指定します。
十字キーで移動してエンターで選ぶことができます。

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

無事プロジェクトが作成できたら、そのプロジェクトのディレクトリに移動してエディターを開きましょう。

$ cd nest-lesson

作成したプロジェクトが以下の画像のようになっていたら完了です。

この手順でプロジェクトが作成できることはわかったと思います。
次からはNestJsの基本構造について理解していきます。

2. main.tsとは?

作成したプロジェクトの中のsrcフォルダーの中を見てみてください。
今回の記事でNestJsの基本構造を理解するのに特に重要なのは、その中のmain.tsapp.service.tsapp.module.tsapp.controller.tsの4つになります。

まずはmain.tsを理解しましょう。

デフォルトの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();

ここでは、見てわかるようにbootstrap()を実行しています。
そして、その実行される関数の中身の中にあるAppModuleというのが、app.module.tsになります。
このmain.tsにすべての処理を設定しているファイルであるapp.module.tsを実行することにより処理が実行されます。
つまり、このNestFactory.createの引数に何も指定しなかったら何も処理が実行されないことになります。

Reactをやっている方は、index.jsxの中でApp.jsxをdivタグに描画する指定をしているものと同じようなものだと考えるとわかりやすいと思います。

3. Moduleとは?

main.tsで読み込まれているAppModuleはこのNestJsプロジェクトのリーダー的な存在です。
このAppModuleの中でこれから紹介するControllerやProviderを設定することができます。

デフォルトのAppModuleは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 {}

ここで指定されている@Moduleという@から始まるものをデコレーターといいます。
このデコレーターは関数の役割を果たします。

このAppModuleの中にはControllerやServiceをインポートすることができ、ここにインポートをすることでそれぞれのコントローラーやサービスの処理が実行されるようになります。

デフォルトでは、ControllersにAppController、ProvidersにAppServiceが指定されています。

importsにインポートするものやその他にここで指定できるexportsというものはこれから説明していくので今は置いておきます。

4. Controllerとは?

AppModuleに指定されていたControllersにあったAppControllerなどのControllerは端的にいうと、「どのようなURLが来たら、どのような値を返すか」ということを定義するものになります。

デフォルトの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()とはデコレーターであり、このデコレーターの下にメソッドを書いてreturnで返すことで、指定されたURLによって実行される処理を指定できます。

デフォルトのControllerの指定では、ルートのURLに行くと、AppServiceというクラスの中のgetHello()を返していることになります。

このgetHello()という関数はAppServiceというクラスに定義されています。

それでは一旦サーバーを起動させて、表示を見てみましょう。
サーバーを起動させるコマンドは以下のようになります。

$ npm run start:dev

問題なく起動できたら、 http://localhost:3000 を開いて確認しましょう。
以下のような画面が出てきたら成功です。

シンプルに「Hello World!」という文字列が表示されました。
つまり、これはAppServiceに定義されているgetHello関数が「Hello World!」という文字列を返しているということになります。

デフォルトのAppServiceを見てみましょう。

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

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

ここではAppServiceクラスのgetHello関数で「Hello World!」という文字列がreturnされています。
つまり、AppControllerの中にAppServiceで定義した関数を実行したことになります。

ということは、原理として、AppServiceを使わずにAppController内で記述した処理でも実行できるということになります。

それではやってみましょう。AppControllerの記述を以下のように変更してみてください。

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();
    return 'こんにちは';
  }
}

constructorとgetHello()をコメントアウトして、「こんにちは」という文字列をreturnしました。
すると以下のように表示されるはずです。

これでControllerの仕組みがわかったと思います。また、次に説明するServiceについてもある程度理解できたのではないでしょうか。

また、このAppControllerを機能させるためにはAppModuleのcontrollersに記述する必要があります。
忘れないようにしましょう。

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

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

5. Providerとは?

先程のControllerの中での説明のように、ProviderとはController内で指定される処理をまとめたものになります。

デフォルトのAppServiceは以下のように記述されています。

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

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

このAppServiceをAppControllerクラス内のconstructorで指定することで、AppService内に記述したgetHello()関数をAppController内で実行できるということになります。

デフォルトのAppControllerは以下のようになっていました。

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();
  }
}

このAppControllerクラスのconstructorにappServiceという名前でAppServiceを指定することで、this.appService.getHello()という書き方でAppServiceで定義したgetHello関数が実行できるようになります。

繰り返しになりますが、ここまでの説明でお分かりのように、AppModuleのProviderに指定されているAppServiceというものは、AppController内で処理する記述をまとめたものということになります。

また、このAppServiceを機能させるためにはAppModuleのprovidersに記述する必要があります。
忘れないようにしましょう。

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

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

簡単にこれまでの基本構造をまとめると以下のような順番に処理されていることになります。

AppService→AppController→AppModule→main.ts

続き→REST ful API編(執筆中)