😎

【入門&考察】NestJSを使ってみた。特徴、メリットデメリットなど

2022/06/20に公開

初記事です。
webエンジニアに転向し、バックエンド&インフラ担当の部署に所属して数ヶ月が経ったのですが、初めて触ったのがNestJSだったので紹介します。

NestJSとは?

Node.jsのフレームワークです。
最新のJavaScriptで記述可ですが、TypeScriptでの記述もサポートしており、後者が主流かと思います。
また、内部的にはexpress.jsの実装に近く、expressのミドルウェアを使用することも可能です。
その他、概念やら色々と紹介する事はありますが、先にコードから。

コード(初期状態)

NestJSのCLIを用いてプロジェクトの雛形を生成します。
(環境構築手順は今回割愛します)
デフォルトのディレクトリ構造はこんな感じになります。
directory

色々ありますが主要なファイルのみ紹介します。

  • app.controller.ts
    ルーティング。ユーザのリクエストを受信し、レスポンスを返却します。expressのミドルウェアに近い印象でした。
    controller

  • app.service.ts
    ルーティングされたリクエストの実処理。APIビジネスロジックに該当します。
    service

  • app.module.ts
    @Moduleデコレータ内で定義して、コントローラやサービスの各クラスを注入します(ココの詳細は次に記載)
    module

DI(Dependency Injection)という概念について

NestJSでは、デザインパターンの1つであるDIを採用しています。直訳すると”依存性の注入”(わかりづらい...)。
オブジェクトを生成するクラス/使用するクラスを分けて、生成したオブジェクトを外から注入するような作りにする事でオブジェクト間の依存関係を弱めます。これが狙いです。
NestJSでは、依存しているクラスをconstructorに注入する事でDIを実現しています。

例えばapp.controller.tsのコンストラクタを見ると、AppServiceインスタンスが注入されていますね。
インスタンスの生成・破棄は(app.module.tsの@Moduleデコレータで定義する事により)DIで行っています。
なので、使用者がインスタンスの管理を行う必要がありません。
依存関係が弱まり、単体テストが行いやすくなります。保守性も上がります。

CLI機能(一部のみ)

NestJSはCLIが優秀だなと感じました。今回1つだけ紹介します。

▼CRUDの雛形を生成する機能
REST/GraphQL/WebSocket/マイクロサービス それぞれに適した雛形を生成可能です。
select_setting

プロジェクトの雛形生成後に"nest g resource"と入力&実行した時の写真です。
(今回はリソース名をusersとしています)
上記それぞれに適した雛形を生成可能です。便利ですよね。
ここでRESTを選択すると、Serviceに対してCRUDの雛形が定義されます。
REST sample

また、GraphQLを選択するとリゾルバに以下のように定義されます。
GraphQL sample
ここでクライアントが操作できるクエリその他様々な型を定義していく流れです。

その他概念・機能

概要だけ紹介します。

  • Middleware
    ルートハンドラの前に呼び出されます。expressミドルウェアと同様の仕組みです。

  • Interceptors
    ミドルウェアと同様、ルートハンドラの前に呼び出されます。
    ただミドルウェアはHTTPのAPIに対してしか使えないようで、こちらはマイクロサービスのAPIに対しても使えます。

  • Pipes
    受け取ったデータに対して以下を行えます。

    • 変換:入力データを目的の形式に変換。
      例えば、数値→文字列に変換したい場合は ParseIntPipe を用います。
    • 検証:入力データを評価し、有効ならば通過させます。無効の場合は例外をスロー。
      今回携わったプロジェクトでは、DTO("Data Transfer Object")クラスを定義して更にバリデーションチェック(ValidationPipe)を行いました。
      Controller.tsにバリデーションチェック処理を直接記述しなくて良いので、コードがスッキリして見やすくなります。前職の組み込み開発でもこんな仕組みが欲しかったな...。
  • Exception Filters
    例外処理レイヤー。例外が発生した時にこのレイヤでキャッチされ、予め定義した例外に応じて処理を変えることが可能です。

  • Guards
    認証機能を搭載できます。ユーザ権限やロール、ACLの検証を行い、満たしていない場合はその場で弾くことも可能です。@Guardデコレータにより呼び出す事ができるので、今回の開発だとController.tsに記述する事で認証チェックを行ってからビジネスロジックに移行してました。

NestJSの良かったところ

  • ディレクトリ構成が定まっているので、エンドポイントやビジネスロジックに集中して取り組める
  • 保守性に優れており、スケールアップが容易に感じた
  • Expressのライブラリを使用できる
  • TypeScriptで書ける
    開発時に安全性がある程度担保されます。
    あと、フロント&バックエンドを同一言語で揃えられる。ココは地味に大きいメリットな気がします。
  • (紹介したとおりですが)CLIが優秀
  • デコレータが豊富
    例)クエリなら、 @Query(key?):req.query / req.query[key]
    初めは少し大変かもですが、可読性の向上に繋がります。
  • 公式ドキュメントの内容が非常に充実している

NestJSの気になったところ

  • TypeScriptで記述可能ですが、コミュニティ等であがっているコードを見る限りだとジェネリクスが使われていなかったり。型安全性に対する意識はやや低めか...?
  • デコレータが多すぎる。あと、仕様がやや複雑なデコレータもある。
    →例えば、Middlewareと@Interceptorデコレータなど。ちなみに処理順も決まっていて、Middleware→@Guard→@Interceptorといった処理順のようです。
  • モジュール間の依存関係がイメージしづらい
    →ファイル数が増加した時にわかりづらくなりそう
  • 柔軟性はexpress.js > NestJS
  • 情報量はまだ少ない。英語記事メイン。

といった感じです。

拙い内容でしたが便利さは伝わりましたでしょうか...?
web業界に本格的に入って初めて触った技術としては凄く面白かったです。
デコレータやDI、構成に慣れれば大規模開発のスケールアップなんかにも向いてそうです。

次回はTypeORMやPrismaを用いて色々やってみます。

Discussion