NestJSのススメ ～Expressを超えてゆけ～

この記事は､[今年イチ！お勧めしたいテクニック by ゆめみ feat.やめ太郎 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/yumemi_no1) 23日目の記事です｡

# 導入
皆さんこんにちは。**らいパン粉**という者です。twitter→https://twitter.com/elipmoc101
さて、皆さんはバックエンドのプログラミング言語に何を選ぶでしょうか。
PHP？Ruby？JavaScript(Node.js)？
それともElixir、Scala、Go、***Rust***、Haskell等でしょうか。**Cool**ですね。 
最近、フロントエンドでは**TypeScript**が有名です。
この際、バックエンドもフロントエンドも**TypeScript**でサクッと開発してみたいと思いません？僕は思いませんけど。(*Ebio Syntax*)
そんなわけで、**TypeScript**でサクッとバックエンド開発ができる**NestJS**を紹介していきます。
*let's npm start~*
# NestJSとは

- Typescript製のバックエンドフレームワーク
- デフォルトではExpressをコアとして動作
- Fastifyをコアとして動作させることもできる
- Node.jsで上で動く
- 実装と疎結合になるようなアーキテクチャ
- nest cli で簡単にプロジェクトやソースファイルのテンプレートを生成できる
- Expressのミドルウェアをそのまま使える
- テストフレームワークが用意されている
- 認証ライブラリはPassportなどが使える
- GraphQLもサポート
- WebSocketももちろんOK
- class-validatorを使って楽々バリデーション
- TypeORMで型の恩恵を最大限に受けつつDB操作できる
- とにかく拡張性が高い

まあ、一番良いと思うのは、**TypeScript**である点とcliファイル生成してサクサク開発できるところですね。

公式サイト
https://docs.nestjs.com/

# おすすめポイントピックアップ

## Expressの便利なアレコレが使える

**NestJS**はデフォルトではExpressをコアとして動作します。
また、Expressの機能は**NestJS**でも使えます。
なので、Expressで動作するライブラリをそのまま活用できる可能性があります。

## 自動で差分コンパイルしてくれる
gulpとかtsc -w を使うまでもない！！

まず以下のコマンドを実行することで、サーバを開発モードで起動できます。

```
npm run start:dev
```
この状態で、ソースファイルを書き換えて保存すると、ファイル監視システムが検知して、差分をビルドしてサーバを再起動してくれます。


## デコレータがお手軽
とりあえず分からない人向けにデコレータについて解説されたQiita記事置いときますね。
https://qiita.com/taqm/items/4bfd26dfa1f9610128bc

**NestJS**では**デコレ―タ**という**TypeScript**の言語機能を使って、様々な機能を簡単に実装できます。
HTTPリクエストに対する処理を例にしましょう。

```ts
@Controller('app')
export class AppController {
  @Get('fuga')
  getFuga(@Query() query: { text: string }): string {
    return query.text
  }
}
```

解説していきます。
まず`@Controller('app')`です。
これは`AppController`クラスが**コントローラ**であることを**NestJS**に伝えています。
引数の`'app'`は**コントローラ**のurlパス名の指定です。
例では、`localhost:3000/app`が`AppController`の領域になります。
次に`@Get('fuga')`の説明です。
これは`getFuga`関数が**Getリクエスト**を処理することを**NestJS**に伝えています。
引数の`'fuga'`はリクエストのurlパス名の指定です。
先ほどの`'app'`に続けて追記されるので、`localhost:3000/app/fuga`に**Getリクエスト**することで、`getFuga`関数が実行されます。
次に`@Query()`です。これを書いてから引数を取ると、**URLクエリパラメータ**が引数に渡ってきます！
`@Query() query: { text: string }`と書くことで、キーが`text`のクエリパラメータを取得できるわけです。
実際にアクセスするとこんな感じになります。

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208277/f2d254ed-3ce9-c704-5f8b-5d1939d18dad.png)
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

## ルーティングとコントローラが一体化してる

### ルーティングとコントローラが分離されているデメリット
**ルーティング**と**コントローラ**の処理が別ファイルとして分離していると、困ることがあります。
実際に具体例を見ていきましょう。

#### CASE．とあるRailsプロジェクトにて

ワイ「今日はhogefuga **コントローラ**の修正やな。まあサクッと終わらせるで」

```
app/controllers/v1/hogefuga_controller.rb
```
ワイ「結構フォルダ階層深いんやな」

ワイ「これのindexメソッドを修正したらええんやな。その前に実際にブラウザでアクセスして動作確認してみるで」
ワイ（このプロジェクトは何故か**単体テストが無いんや**。だから動作確認は手動でするしかあらへん）

`localhost:3000/v1/hogefuga`にアクセスする
ワイ「あれ？404エラーや。しゃーない。一回**ルーティング**の定義見に行くやで」

```
/app
/bin
/config
/db
/lib
/log
/public
/spec
/storage
/tmp
```
ワイ「あれ**ルーティング**定義してるファイルどこやったっけ？`app`じゃないし、`bin`じゃないし、`db`でもないやろし」
ワイ「そもそもワイは何をしたかったんやっけ？（記憶消去）」
1分後
ワイ「ああ！`config`の`routes.rb`やったわ！」
ワイ「うへぇ……`routes.rb`ごちゃごちゃしててわかりずらいで」
1分後
ワイ「何とか見つけた……。`v1/hoge/fuga/get_hogefuga`に**Getリクエスト**や。なんでこんなurl設計になってんねん！」
ワイ「もはや**RESTful API**じゃなくて、**Stressful API**やな」


このように、**ルーティング**定義と**コントローラ**が分かれていると、行き来するケースがあり大変です。
**認知負荷**が高いですね！！
これが**ルーティング**と**コントローラ**が分かれているデメリットです。（もちろん、**ルーティング**の責務を**コントローラ**から分離するとメリットもありますので、トレードオフですが）

**NestJS**では、**コントローラ**で**ルーティング**を定義してしまいます。

```ts
import { Controller, Get } from '@nestjs/common';

@Controller('v1/hoge/fuga')
export class HogefugaController {

    @Get('get_hogefuga')
    index() {
        return 'hogehogefugafuga'
    }
}
```
すでに`@Controller`と`@Get`については説明済みなので、説明は省略します。

## アーキテクチャがいい感じ
![a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208277/3d222cce-e102-da83-14dd-27d3eb682574.png)

これは局所的な例なので、必ずしもこうとは限りませんが、**NestJS**では上の図のような構成で開発をすることが可能です。（図の意味が分からない人はクラス図を勉強してください）

依存関係の分離とかは**DIコンテナ**がいい感じにやってくれます。
**DIコンテナ**について解説したQiita記事置いときます。
https://qiita.com/hinom77/items/1d7a30ba5444454a21a8
https://qiita.com/ritukiii/items/de30b2d944109521298f

各責務を紹介します

- Module
    - 依存関係を管理する責務
- Controller
    - ユーザからのリクエストに答える責務
    - 実際の処理はServiceに任せる
- Service
    - 各種機能をControllerに提供する責務
- Repository
    - データの永続化をする責務


以下は実装の例です。

![b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208277/403a0d21-42b7-e305-0f09-49750f734ca7.png)

この設計のメリットは、主要ロジックがインフラストラクチャに依存することを防ぐことができるところでしょうか。

## テストが楽
**NestJS**では、単体テストはかなり楽にできます。
まずcliで**コントローラ**や**サービス**などを生成していくわけですが、同時にテストファイルも生成してくれるので最高です。
さらに、アーキテクチャや**DIコンテナ**のおかげで**リポジトリ**や**サービス**の実装などをテスト用にすり替えるのが非常に簡単になっています。

# インストール～プロジェクト作成～DB操作まで
手を動かしてやってみた方が速い！！
というわけで一通り、やってみましょう！

まずは適当にフォルダを作ります。
今回は`nest_test`という空フォルダを作り、そこを利用します。
`-g`で`npm install`はしないので、そこはご了承ください。

## インストールとプロジェクト作成

```
cd nest_test
npm i @nestjs/cli
npx nest new .
```

`nest_test`フォルダに移動したら、**NestJS**用のCliをインストールします。
`nest new [プロジェクトフォルダ名]`でプロジェクトを作成できるのですが、`.`を入力することで、`nest_test`をそのままプロジェクトとして作成します。

`nest new`の詳細な情報
https://docs.nestjs.com/cli/usages#nest-new

## サーバの起動

```
npm run start:dev
```
このコマンドを打つことによって開発用サーバが起動します。
デフォルトでは`localhost:3000`になります。
アクセスするとこうなります。

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208277/defc260c-0567-393f-e0b5-de9bc5acdec9.png)
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー


これからは、サーバを停止した状態を前提として解説するので注意してください。
（暗黙的な自動コンパイルは教える際の障害となるため）

## TypeORMとSqliteのセットアップ

DBを操作するためのライブラリとして**TypeORM**を使っていきます。
DBはお手軽に**SQLite**にしました。
必要なものをインストールしていきます。

```
npm i --save @nestjs/typeorm typeorm sqlite3
```

そして**TypeORM**にDBの情報を教えるため、**TypeORM**の設定ファイルを用意します。
プロジェクト直下に`ormconfig.json`というファイルを作成しましょう。

```json:nest_test/ormconfig.json
{
    "type": "sqlite",
    "database": "data/dev.sqlite",
    "entities": [
        "dist/entities/**/*.entity.js"
    ],
    "migrations": [
        "dist/migrations/**/*.js"
    ],
    "logging": true
}
```

`"type"`にはDBの種類を指定します。
`"mysql"`, `"postgres"`, `"cockroachdb"`, `"mariadb"`, `"sqlite"`, `"oracle"`, `"mssql"`, `"mongodb"`, `"sqljs"`など、指定できるDBはかなり多いです。
https://github.com/typeorm/typeorm/blob/master/docs/connection-options.md#connection-options-example

`"database"`にはDBの名前を指定します。
`"entities"`にはテーブル定義となる**Entity**が定義されたjsがある場所を指定します。（**Entity**については後述）
今回は`dist/entities`配下に**Entity**が定義されたjsを生成するようにします。
`"migrations"`にはマイグレーションファイルがある場所を指定します。
今回は`dist/migrations`配下にマイグレーションファイルのjsを生成するようにします。
`"logging"`を`true`に設定することで、実行したSQL文をLogとして確認できるようになります。これはデバックする際の良いヒントとなります。

次に**NestJS**に**TypeORM**を組み込んでいきます。`app.module.ts`に追記します。

```ts:nest_test/src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm'; //追加！

@Module({
  imports: [TypeOrmModule.forRoot()], //追加！
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule { }
```

この設定により、全ての**モジュール**から**TypeORM**の機能を呼び出せるようになります。

## Entityを定義

テーブルを作成するため、**Entity**を1つ定義してみます。
**TypeORM**における**Entity**とは、テーブルのデータ構造を、**クラス構文で表現したオブジェクト**のことです。
**Entity**を作成することで、**テーブルの定義**を行うことができます。

まず`nest_test/src/entities/memo.entity.ts`ファイルを生成します。
そして以下のようにコードを書いてください。

```ts:nest_test/src/entities/memo.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Memo {
    @PrimaryGeneratedColumn()
    id: number;

    @Column({ length: 500 })
    name: string;

    @Column('text')
    description: string;
}
```

`@Entity()`を付けたクラスは**Entity**となり、**memoテーブルの定義**となります。
`@PrimaryGeneratedColumn()`をクラスの変数に付けると、それは**自動増分主キーのカラム**となります。
`@Column()`をクラスの変数に付けると、それは**カラム**として登録され、`@column('text')`のように属性を指定したりもできます。
これで、**Entity**の作成完了です。

## マイグレーションする

**マイグレーション**を行い、DBにテーブルを作成していきたいと思います。

まずは**マイグレーションファイル**を作成します。
先ほど作った**Entity**から**マイグレーションファイル**を自動生成することができるので、それをやります。

```
npm run build
npx typeorm migration:generate -d src/migrations -n create-memo
```

`npm run build`で`dist/entities`配下に`memo.entity.js`を出力し、**TypeORM**のコマンドで、`create-memo`という名前で**マイグレーションファイル**を作成しています。

生成された**マイグレーションファイル**はこんな感じです。

```ts:nest_test/src/migrations/1575868486667-create-memo.ts
import {MigrationInterface, QueryRunner} from "typeorm";

export class createMemo1575868486667 implements MigrationInterface {
    name = 'createMemo1575868486667'

    public async up(queryRunner: QueryRunner): Promise<any> {
        await queryRunner.query(`CREATE TABLE "memo" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(500) NOT NULL, "description" text NOT NULL)`, undefined);
    }

    public async down(queryRunner: QueryRunner): Promise<any> {
        await queryRunner.query(`DROP TABLE "memo"`, undefined);
    }

}
```

次に**マイグレーションファイル**の実行をします。

```
npm run build
npx typeorm migration:run
```
動作としては、`dist/migrations`に生成された**マイグレーションファイル**を実行しているだけです。

## モジュール、コントローラ、サービス、リポジトリの実装

次にMemoに関する**モジュール**、**コントローラ**、**サービス**を作っていきます。

```
npx nest g mo memo
npx nest g co memo
npx nest g s memo 
```
`nest g`の詳細な情報
https://docs.nestjs.com/cli/usages#nest-generate

`nest_test/src/memo`フォルダ配下に以下の三つのファイルが生成されていることが確認できると思います。

- memo.module.ts
- memo.controller.ts
- memo.service.ts

次にMemoテーブルを操作をする**リポジトリ**を実装and登録します。
なんと！**TypeORM**が**リポジトリ**の実装を自動でやってくれます！（さらに実装された**リポジトリ**を継承して**カスタムリポジトリ**も作れるらしい　https://docs.nestjs.com/techniques/database#custom-repository）

`memo.module.ts`に追記します。

```ts:nest_test/src/memo/memo.module.ts
import { Module } from '@nestjs/common';
import { MemoService } from './memo.service';
import { Memo } from 'src/entities/memo.entity'; //追加！
import { TypeOrmModule } from '@nestjs/typeorm'; //追加！
import { MemoController } from './memo.controller';

@Module({
  controllers: [MemoController],
  imports: [TypeOrmModule.forFeature([Memo])], // 追加！
  providers: [MemoService]
})
export class MemoModule { }

```

Memoサービスに登録した**リポジトリ**を注入します。
コンストラクタの引数で`@InjectRepository(Memo)`と書けば、**DIコンテナ**が、**登録されたMemoリポジトリを生成して渡してくれます。**

```ts:nest_test/src/memo/memo.service.ts
import { Injectable } from '@nestjs/common';
import { Memo } from 'src/entities/memo.entity'; //追加！
import { Repository } from 'typeorm'; //追加！
import { InjectRepository } from '@nestjs/typeorm'; //追加！

@Injectable()
export class MemoService {
    constructor(
        @InjectRepository(Memo) //追加！
        private readonly memoRepository: Repository<Memo> //追加！
    ) { }
}
```

Memoサービスを実装していきます。

```ts:nest_test/src/memo/memo.service.ts
import { Injectable } from '@nestjs/common';
import { Memo } from 'src/entities/memo.entity';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';

@Injectable()
export class MemoService {
    constructor(
        @InjectRepository(Memo)
        private readonly memoRepository: Repository<Memo>
    ) { }

    addMemo(name: string, description: string) {
        const memo = new Memo()
        memo.name = name;
        memo.description = description;
        return this.memoRepository.insert(memo);
    }

    getMemoList() {
        return this.memoRepository.find();
    }
}
```

MemoコントローラーにMemoサービスを注入してMemoサービスの関数を利用し、**Get・Postリクエスト**を実装していきます。
コチラも**DIコンテナ**のおかげで`MemoService`型の引数があれば、**勝手に生成して渡してくれます。**
**DIコンテナ**は本当に素晴らしいですね！我々は**生成の責務**を考えないで実装に専念できます！！

```ts:nest_test/src/memo/memo.controller.ts
import { Controller, Get, Post, Query } from '@nestjs/common';
import { MemoService } from './memo.service';

@Controller('memo')
export class MemoController {
    constructor(private readonly service: MemoService) { }

    @Get()
    getMemoList() {
        return this.service.getMemoList();
    }

    @Post()
    addMemo(@Query() query: { name: string, description: string }) {
        return this.service.addMemo(query.name, query.description);
    }
}
```

## 動作確認

```
npm run start:dev
```
`localhost:3000/memo`に**Getリクエスト**すると、空の配列が返ってきます。
`localhost:3000/memo?name=fuga&description=hogehoge`に**Postリクエスト**し、再度`localhost:3000/memo`に**Getリクエスト**すると、memoが一つ追加されているのが確認できます。

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208277/721e03eb-5188-efdd-53b1-d983ebbbed48.png)
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

# 結論
これからはExpressを捨てて**NestJS**の時代かも？！


あと、書いてから気づいたんですが、**NestJS**のアドベントカレンダーありました。（**NestJS**のマニアックな話知りたい人は是非）
https://qiita.com/advent-calendar/2019/nestjs

#参考
NestJS公式サイト
https://docs.nestjs.com/
TypeORM公式サイト
https://typeorm.io/
TypeORMのREADME
https://github.com/typeorm/typeorm/blob/master/README.md
Qiita Nest.jsは素晴らしい
https://qiita.com/kmatae/items/5aacc8375f71105ce0e4
TypeScriptによるデコレータの基礎と実践
https://qiita.com/taqm/items/4bfd26dfa1f9610128bc
DIとDIコンテナを3分で理解する
https://qiita.com/hinom77/items/1d7a30ba5444454a21a8
DI・DIコンテナ、ちゃんと理解出来てる・・？
https://qiita.com/ritukiii/items/de30b2d944109521298f