見出し画像

今日からはじめるNest.js入門

1.Nest.jsとは

フレームワークを理解するには、その思想や概念を把握することが非常に重要です。
そのため、公式サイトのイントロダクションやフィロソフィーを参照してみましょう。

●Introduction
Nest(NestJS)は、効率的でスケーラブルなNode.jsサーバサイドアプリケーションを構築するためのフレームワークです。プログレッシブJavaScriptを採用し、TypeScriptを完全にサポートし、OOP(オブジェクト指向プログラミング)、FP(機能的プログラミング)、FRP(機能的反応プログラミング)の要素を兼ね備えています。

Nestは、デフォルトでExpressのような堅牢なHTTPサーバーフレームワークを使用し、オプションでFastifyを使用するように設定することができます。

Nestは、これらの一般的なNode.jsフレームワーク(Express/Fastify)よりも抽象化されたレベルを提供しますが、同時にそれらのAPIを開発者に直接公開します。これにより、開発者は、基盤となるプラットフォームで利用可能な無数のサードパーティモジュールを自由に使用することができます。

Philosophy
近年、Node.jsのおかげで、JavaScriptはフロントエンドとバックエンドの両方のアプリケーションにおいて、Webの「共通言語」となっています。このため、Angular、React、Vueといった素晴らしいプロジェクトが生まれ、開発者の生産性を向上させ、高速でテスト可能、かつ拡張性の高いフロントエンドアプリケーションの作成を可能にしています。

しかし、Node(およびサーバーサイドJavaScript)には優れたライブラリやヘルパー、ツールが数多く存在するものの、アーキテクチャという主要な問題を効果的に解決するものはありません。

Nestは、開発者やチームが高度にテスト可能で、スケーラブルで、疎結合で、簡単に保守できるアプリケーションを作成できるようにする、すぐに使えるアプリケーションアーキテクチャを提供します。このアーキテクチャは、Angularに大きく影響を受けています。

https://docs.nestjs.com/

とあります。
Nest.jsは次のような特徴を持っているとされています。

● Node.js製のサーバーサイドフレームワーク
● TypeScriptをdefaultでサポート
● Express(Fastify)をより抽象化したもの
● Angularに強く影響を受けている
● DI(Dependency Injection) 依存性の注入ができてテストが行いやすい
● REST APIサーバー、GraphQLサーバーとしても活躍できる

https://docs.nestjs.com/

この記事ではNest.jsの概念を理解するために焦点を当てています。

Nest.jsはサーバーサイドフレームワークであり、AWSのElastic BeanstalkやEC2、GCPのAppEngineやCloud Runなどのようなサーバーサイドで動作するRailsやLaravelと同様のフレームワークです。

Nest.jsはモノリシックなアプリケーションを作成することもできますし、フロントエンドとバックエンドが分離したアーキテクチャでバックエンドを担当することも可能です。
従来、Node.jsではExpressが主流でしたが、Expressはミドルウェア的な位置づけであり、完全なサーバーサイドフレームワークとは言いにくい側面がありました。
Nest.jsの登場により、Node.jsの世界でも堅牢なサーバーサイドフレームワークが提供されるようになりました。

2.Nest.jsのストラクチャー構造

では、もう少し具体的にNest.jsの機能を見ていきましょう。

// CLIをインストールする
$ npm i -g @nestjs/cli

// Nest.jsプロジェクトを作成する
$ nest new nest-sample

// サーバーを起動する
$ cd nest-sample
$ npm run start:dev

プロジェクトを作成し、サーバーを起動するとlocalhost:3000で"Hello World"が表示されます。
では、プロジェクトのストラクチャーを見てみましょう。

src
├── main.ts
├── app.controller.spec.ts
├── app.controller.ts
├── app.module.ts
├── app.service.ts

srcディレクトリ以下にはmain.tsとapp module関連のファイルが配置されています。

├── main.ts // nest.jsのエントリーポイント、サーバのインスタンスを作成しlistenしている
├── app.controller.spec.ts // controllerのテストファイル
├── app.controller.ts // ルーティングと、どの処理へと繋ぐかを判断するcontrollerファイル
├── app.module.ts // controllerとserviceをまとめるmoduleファイル
├── app.service.ts // MVC的に言うとMの部分で処理のメインロジックを担当するファイル

Nest.jsでは、controllerとserviceをモジュールに読み込んで使用しています。それぞれの中身を見ていきましょう。

// main.ts

async function bootstrap() {

  // create()の引数にはルートモジュールを指定
  const app = await NestFactory.create(AppModule);
  
  // 3000番ポートでサーバを起動する
  await app.listen(3000);
}
bootstrap();
// app.controller.ts
// controller作成初期はここまで存在しませんが代表的な書き方を列挙しておきます。

import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';

// Controllerデコレーター
// @Controller('hello')のように引数でもパス指定が可能
@Controller()
export class AppController {
	
  // constructorに入れることで自動でDIする
  constructor(private readonly appService: AppService) {}

  // Getメソッドの場合はここで受け取る
  // @Get('hello')のように引数でもパス指定が可能。
  @Get()
  findAll(): string {
    // 実際の処理はservice側で行う
    return this.appService.findAll();
  }

  // :idとすることで/123や/3826などパラメーターを動的に受け取る
  @Get(':id')
  findOne(@Param() params): string {
    console.log(params.id);
    return `paramsのidは${params.id}です。`;
  }

  // /search?id=4や/search?id=4&word=今日の天気、のようなクエリーを受け取る
  @Get('search')
  findQuery(@Query() query: { id: string, word: string }) {
    return `idは${query.id}で、wordは${query.word}です。`;
  }

  // BodyやHeaderなど様々なリクエストデータを受け取る
  @Get()
  findRequest(@Req() request: Request): string {
    console.log(request);
    return 'リクエストを受け取りました';
  }

  // Postメソッドの場合ここでリクエストBodyを受け取る
  // create(@Body: createDto: CreateCatDto): string のように、
  // dtoを引数で指定できそのままreturn this.appService.create(createDto);とすることができる
  @Post()
  create(@Body: id: number, @Body name: string, @Body email: string): string {
    return this.appService.create(id, name, email);
  }

  // idを受け取って更新する
  @Put(':id')
  update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto) {
    return this.appService.update(id);
  }

  // idを受け取って削除する
  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.appService.delete(id);
  }
}
// app.service.ts

// @injectableとすることでproviderとして認識されてDIできるようになる
@Injectable()
export class AppService {

  // 各ロジックを記述していく
  findAll(): string {
    return 'Hello World!';
  }

  findOne(id: string): string {
    return '...';
  }
}
// app.module.ts

// このアプリのルートモジュール
@Module({
  // importsの配列の中に他のmoduleを入れると別のmoduleをimportすることが可能になる
  imports: [], // [userModule, AuthorModule, productModule]
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

アプリを作る際には、ルートごとにディレクトリが切られます。
具体的には、/users/itemsなどのルートに対応するディレクトリが作成されます。
以下のファイルを1つの基本セットとして作成していきましょう。
それでは、以下のように/users/userのファイル郡を作成してみましょう。

├── main.ts
├── app.controller.spec.ts
├── app.controller.ts
├── app.module.ts
├── app.service.ts
├── users // usersルーティングの処理
    ├── dto
    │   ├── create-user.dto.ts
    │   └── update-user.dto.ts
    ├── entities
    │   └── user.entity.ts
    ├── users.controller.spec.ts
    ├── users.controller.ts
    ├── users.module.ts
    ├── users.service.spec.ts
    └── users.service.ts

dtoentitiesは、Nest.jsにおけるディレクトリの一部です。
以下でそれぞれの役割を説明します。

● dto: DTO(Data Transfer Object)
データの転送や通信のためのオブジェクトです。このディレクトリでは、データの受け渡しに関連するクラスやインターフェースを定義します。通常、外部からのリクエストのバリデーションやレスポンスの形式などを定義するために使用されます。

● entities: entities
データベースのテーブルやコレクションに対応するクラスやデータモデルを定義するためのディレクトリです。データベースとのやり取りや操作を担当するオブジェクトがここで定義されます。通常、ORM(Object-Relational Mapping)やODM(Object-Document Mapping)を使用してデータベースとの連携を行います。

ORMを使用する場合、Prismaなどが提供するツールを使ってデータベーススキーマから生成されたモデルやクラスを使用します。このため、entitiesディレクトリで独自にエンティティを定義する必要はありません。

Nest.jsでは、認証処理やエラー処理、リクエストやレスポンスに対して行いたい共通の処理であるミドルウェアを簡単に導入することができます。
ミドルウェアは、アプリケーション全体や特定のエンドポイントに対して、リクエストが処理される前後で追加の処理を実行するために使用されます。例えば、リクエストの認証や承認、データの検証、エラーハンドリングなどの処理をミドルウェアで行うことができます。

3.Nest.jsの概念図

Nest.jsの概念図

ストラクチャーを深堀りして見えてきた構成図は以下のようになります。

● クライアントからリクエストを受け取る。
● リクエストはガードやパイプなどのミドルウェア層を通過する。
● DTO(データ転送オブジェクト)によるバリデーションが行われ、バリデーションが成功した場合、ルートのApp Moduleに到達する。
● マッチしたモジュールが呼び出され、ルーティングされたコントローラーに到達する。
● ルーティングされたコントローラーは、各サービスやリポジトリを介してデータベースや外部APIなどに接続し、さまざまなロジックを使用してデータを処理する。

Nest.jsでは、前述のような構成でアプリケーションが動作しています。

今回はミドルウェアについては省略しましたが、サーバーサイドで認証や認可が必要な場合は欠かせない要素ですので、一緒に学んでいきたいと思います。
ミドルウェアを使用することで、リクエストやレスポンスに対して追加の処理や検証を行うことができます。
例えば、認証ミドルウェアを組み込むことで、リクエストを受ける前にユーザーの認証状態を確認し、必要な権限を持っているかをチェックすることができます。

Nest.jsのミドルウェアについても一緒に学んでいくことで、よりセキュアで柔軟なサーバーアプリケーションを構築することができるでしょう。

それでは。


この記事が気に入ったらサポートをしてみませんか?