10
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

mofmofAdvent Calendar 2023

Day 11

nest generateコマンドからざっくりとNestJSを把握する

Last updated at Posted at 2023-12-10

この記事は「mofmof Advent Calendar 2023」11日目の記事です。

株式会社mofmofは受託開発会社ですが、メンバー自らがオーナーとなってプロダクト開発にチャレンジしています。
この記事は、NestJS, 生成AIにチャレンジする「金曜日のチーム開発」の成果です。

https://indie-dev.mof-mof.co.jp/

今回学習したNestJSと生成AIを使って作ったサービスはこちら
よかったら使ってみてください!

はじめに

NestJSのCLIコマンドnest generateで生成されるファイル・ディレクトリから、NestJSプロジェクトの構成やNestJSの敷いてくれるレールをざっくりと把握します。

プロジェクト作成

$ npm i -g @nestjs/cli
$ nest new hello_nest
⚡  We will scaffold your app in a few seconds..

? Which package manager would you ❤️  to use? npm
CREATE hello_nest/.eslintrc.js (663 bytes)
CREATE hello_nest/.prettierrc (51 bytes)
CREATE hello_nest/README.md (3340 bytes)
CREATE hello_nest/nest-cli.json (171 bytes)
CREATE hello_nest/package.json (1951 bytes)
CREATE hello_nest/tsconfig.build.json (97 bytes)
CREATE hello_nest/tsconfig.json (546 bytes)
CREATE hello_nest/src/app.controller.ts (274 bytes)
CREATE hello_nest/src/app.module.ts (249 bytes)
CREATE hello_nest/src/app.service.ts (142 bytes)
CREATE hello_nest/src/main.ts (208 bytes)
CREATE hello_nest/src/app.controller.spec.ts (617 bytes)
CREATE hello_nest/test/jest-e2e.json (183 bytes)
CREATE hello_nest/test/app.e2e-spec.ts (630 bytes)

✔ Installation in progress... ☕

🚀  Successfully created project hello_nest
👉  Get started with the following commands:

$ cd hello_nest
$ npm run start

                                         
                          Thanks for installing Nest 🙏
                 Please consider donating to our open collective
                        to help us maintain this package.
                                         
                                         
               🍷  Donate: https://opencollective.com/nest
$ tree -I node_modules
.
├── README.md
├── nest-cli.json
├── package-lock.json
├── package.json
├── src
│   ├── app.controller.spec.ts
│   ├── app.controller.ts
│   ├── app.module.ts
│   ├── app.service.ts
│   └── main.ts
├── test
│   ├── app.e2e-spec.ts
│   └── jest-e2e.json
├── tsconfig.build.json
└── tsconfig.json

nest generateコマンド

$ npx nest g <schematic> <name> [options]

Schematics

名前 エイリアス 説明
app モノレポ内に新しいアプリケーションを生成 (標準構造の場合はモノレポに変換)。
library lib モノレポ内に新しいライブラリを生成 (標準構造の場合はモノレポに変換)。
class cl 新しいクラスを生成。
controller co コントローラー宣言を生成。
decorator d カスタムデコレーターを生成。
filter f フィルター宣言を生成。
gateway ga ゲートウェイ宣言を生成。
guard gu ガード宣言を生成。
interface itf インターフェースを生成。
interceptor itc インターセプター宣言を生成。
middleware mi ミドルウェア宣言を生成。
module mo モジュール宣言を生成。
pipe pi パイプ宣言を生成。
provider pr プロバイダー宣言を生成。
resolver r リゾルバー宣言を生成。
resource res 新しいCRUDリソースを生成。
service s サービス宣言を生成。

app

nest newした時に自動生成されるものと同じ。
開発の基盤となるアプリケーションの基本的な構造、主要なモジュール、コントローラー、サービスが含まれる。

generateコマンド実行結果
$ nest g app
? What name would you like to use for the app? 
DELETE src
DELETE test
CREATE apps/hello_nest/tsconfig.app.json (225 bytes)
CREATE apps/hello_nest/src/app.controller.spec.ts (617 bytes)
CREATE apps/hello_nest/src/app.controller.ts (274 bytes)
CREATE apps/hello_nest/src/app.module.ts (249 bytes)
CREATE apps/hello_nest/src/app.service.ts (142 bytes)
CREATE apps/hello_nest/src/main.ts (208 bytes)
CREATE apps/hello_nest/test/app.e2e-spec.ts (630 bytes)
CREATE apps/hello_nest/test/jest-e2e.json (183 bytes)
CREATE apps/app/tsconfig.app.json (218 bytes)
CREATE apps/app/src/main.ts (208 bytes)
CREATE apps/app/src/app.controller.spec.ts (617 bytes)
CREATE apps/app/src/app.controller.ts (274 bytes)
CREATE apps/app/src/app.module.ts (249 bytes)
CREATE apps/app/src/app.service.ts (142 bytes)
CREATE apps/app/test/jest-e2e.json (183 bytes)
CREATE apps/app/test/app.e2e-spec.ts (630 bytes)
UPDATE tsconfig.json (562 bytes)
UPDATE package.json (2026 bytes)
UPDATE nest-cli.json (813 bytes)

モノレポに変換された!

❯ tree -I node_modules
.
├── README.md
├── apps
│   ├── app
│   │   ├── src
│   │   │   ├── app.controller.spec.ts
│   │   │   ├── app.controller.ts
│   │   │   ├── app.module.ts
│   │   │   ├── app.service.ts
│   │   │   └── main.ts
│   │   ├── test
│   │   │   ├── app.e2e-spec.ts
│   │   │   └── jest-e2e.json
│   │   └── tsconfig.app.json
│   └── hello_nest
│       ├── src
│       │   ├── app.controller.spec.ts
│       │   ├── app.controller.ts
│       │   ├── app.module.ts
│       │   ├── app.service.ts
│       │   └── main.ts
│       ├── test
│       │   ├── app.e2e-spec.ts
│       │   └── jest-e2e.json
│       └── tsconfig.app.json
├── nest-cli.json
├── package-lock.json
├── package.json
├── tsconfig.build.json
└── tsconfig.json

library

モノレポ内でライブラリの雛形(モジュールやサービス等)を生成する。

generateコマンド実行結果
$ nest g library
? What name would you like to use for the library? hoge
? What prefix would you like to use for the library (default: @app)? 
CREATE libs/hoge/tsconfig.lib.json (218 bytes)
CREATE libs/hoge/src/index.ts (63 bytes)
CREATE libs/hoge/src/hoge.module.ts (182 bytes)
CREATE libs/hoge/src/hoge.service.spec.ts (446 bytes)
CREATE libs/hoge/src/hoge.service.ts (88 bytes)
UPDATE nest-cli.json (1040 bytes)
UPDATE package.json (2140 bytes)
UPDATE tsconfig.json (678 bytes)

ルートにlibsディレクトリが生成される。package.jsonのprojectsにも追加されていた。

$ cd libs/
$ tree .
.
└── hoge
    ├── src
    │   ├── hoge.module.ts
    │   ├── hoge.service.spec.ts
    │   ├── hoge.service.ts
    │   └── index.ts
    └── tsconfig.lib.json

class

新しいクラスを生成。

generateコマンド実行結果
$ nest g class hoge
? Which project would you like to generate to? hello_nest [ Default ]
CREATE apps/hello_nest/src/hoge/hoge.spec.ts (139 bytes)
CREATE apps/hello_nest/src/hoge/hoge.ts (21 bytes)

controller

アプリケーションのルーティングを管理し、リクエストを受け取りレスポンスを返すコントローラーの雛形を生成。

実行結果
$ nest g controller hoge
? Which project would you like to generate to? hello_nest [ Default ]
CREATE apps/hello_nest/src/hoge/hoge.controller.spec.ts (478 bytes)
CREATE apps/hello_nest/src/hoge/hoge.controller.ts (97 bytes)
UPDATE apps/hello_nest/src/app.module.ts (322 bytes)

decorator

クラス、メソッド等への追加機能やメタデータを提供するためのカスタムデコレーターの雛形を生成。

NestJSでは沢山のデコレーターが利用されており、自分で定義したデコレーターも使える。
複数デコレーターを適用する場合は下から順番に関数が実行されることに注意!

hoge.decorator.ts
import { Logger, SetMetadata } from '@nestjs/common';

export const Hoge = (...args: string[]) => {
  Logger.log('Hoge decorator called');
  return SetMetadata('hoge', args); // hogeというkeyに対してデコレーターの引数で受け取った値をセット
};
example.service.ts
import { Injectable, Logger, Reflector } from '@nestjs/common';
import { Hoge } from './hoge.decorator';

@Injectable()
export class ExampleService {
  constructor(private reflector: Reflector) {}

  @Hoge('fuga')
  someMethod() {
    const metadata = this.reflector.get<string[]>('hoge', this.someMethod);
    Logger.log(metadata); // fugaがログに出力される
  }
}
generateコマンド実行結果
$ nest g decorator hoge
? Which project would you like to generate to? hello_nest [ Default ]
CREATE apps/hello_nest/src/hoge/hoge.decorator.ts (117 bytes)
hoge.decorator.ts
import { SetMetadata } from '@nestjs/common';

export const Hoge = (...args: string[]) => SetMetadata('hoge', args);

filter

アプリケーションで発生する例外をキャッチし、カスタムエラーレスポンスを提供するフィルターを生成。

filterは特定の例外に対してグローバルに適用したり、特定のコントローラー等に適用したりすることができる。

hoge.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';

@Catch(HttpException)
export class HogeFilter<HttpException> implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const status = exception.getStatus();

    response
      .status(status)
      .json({
        statusCode: status,
        timestamp: new Date().toISOString(),
        path: ctx.getRequest().url,
      });
  }
}

グローバルに適用する場合はmain.tsでuseGlobalFiltersにインスタンスを渡す。

main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { HogeFilter } from './common/filters/hoge.filter';

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

特定のコントローラー等に適用する場合はデコレーターを使う。

example.controller.ts
import { Controller, Get, UseFilters } from '@nestjs/common';
import { HogeFilter } from './common/filters/hoge.filter';

@Controller('example')
@UseFilters(new HogeFilter())
export class ExampleController {
  @Get()
  someMethod() {
    // ...
  }
}
generateコマンド実行結果
$ nest g filter hoge
? Which project would you like to generate to? hello_nest [ Default ]
CREATE apps/hello_nest/src/hoge/hoge.filter.spec.ts (164 bytes)
CREATE apps/hello_nest/src/hoge/hoge.filter.ts (186 bytes)
hello.filter.ts
import { ArgumentsHost, Catch, ExceptionFilter } from '@nestjs/common';

@Catch()
export class HogeFilter<T> implements ExceptionFilter {
  catch(exception: T, host: ArgumentsHost) {}
}

gateway

WebSocketベースの通信を処理するためのゲートウェイを生成。
チャットなどのリアルタイム通信を実現するために使う。

generateコマンド実行結果
$ nest g gateway hoge
? Which project would you like to generate to? hello_nest [ Default ]
CREATE apps/hello_nest/src/hoge/hoge.gateway.spec.ts (446 bytes)
CREATE apps/hello_nest/src/hoge/hoge.gateway.ts (238 bytes)
UPDATE apps/hello_nest/src/app.module.ts (386 bytes)
hoge.gateway.ts
import { SubscribeMessage, WebSocketGateway } from '@nestjs/websockets';

@WebSocketGateway()
export class HogeGateway {
  @SubscribeMessage('message')
  handleMessage(client: any, payload: any): string {
    return 'Hello world!';
  }
}

guard

特定のルートへのアクセスを制御するためのガードを生成。
認証や認可などのセキュリティ機能を実装。

auth.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    return this.validateRequest(request);
  }

  private validateRequest(request): boolean {
    // ここでリクエストを検証してbooleanで返す
    return request.session && request.session.user;
  }
}
example.controller.ts
import { Controller, UseGuards } from '@nestjs/common';
import { AuthGuard } from './guards/auth.guard';

@Controller('example')
@UseGuards(AuthGuard)
export class ExampleController {
  // ...
}
generateコマンド実行結果
$ nest g guard hoge
? Which project would you like to generate to? hello_nest [ Default ]
CREATE apps/hello_nest/src/hoge/hoge.guard.spec.ts (160 bytes)
CREATE apps/hello_nest/src/hoge/hoge.guard.ts (299 bytes)
hoge.guard.ts
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class HogeGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    return true;
  }
}

interface

TypeScriptのインターフェースを生成。

generateコマンド実行結果
$ nest g interface hoge
? Which project would you like to generate to? hello_nest [ Default ]
CREATE apps/hello_nest/src/hoge/hoge.interface.ts (25 bytes)

interceptor

リクエストやレスポンスを処理する前後に追加の処理(ロギングやエラー処理など)を実行するインターセプターを生成。

logging.interceptor.ts
import { Injectable, Logger, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const now = Date.now();
    const method = context.getHandler().name; // メソッド名を取得
    const className = context.getClass().name; // クラス名を取得

    // 実行前のログ
    Logger.log(`Before... Method: ${method}, Class: ${className}`);
    
    return next
      .handle()
      .pipe(
        // 実行後のログ(実行時間を記録)
        tap(() => Logger.log(`After... Method: ${method}, Class: ${className}, ${Date.now() - now}ms`)), 
      );
  }
}

グローバルに適用する場合はmain.tsでuseGlobalInterceptorsにインスタンスを渡す。

main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { LoggingInterceptor } from './interceptors/logging.interceptor';

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

特定のコントローラー等に適用する場合はデコレーターを使う。

example.controller.ts
import { Controller, Get, UseFilters } from '@nestjs/common';
import { LoggingInterceptor } from './interceptors/logging.interceptor';

@Controller('example')
@UseInterceptors(LoggingInterceptor)
export class ExampleController {
  @Get()
  someMethod() {
    // ...
  }
}
generateコマンド実行結果
$ nest g interceptor hoge
? Which project would you like to generate to? hello_nest [ Default ]
CREATE apps/hello_nest/src/hoge/hoge.interceptor.spec.ts (184 bytes)
CREATE apps/hello_nest/src/hoge/hoge.interceptor.ts (310 bytes)
hoge.interceptor.ts
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class HogeInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle();
  }
}

middleware

リクエスト処理パイプラインにカスタムロジックを追加するためのミドルウェアを生成。

generateコマンド実行結果
$ nest g middleware hoge
? Which project would you like to generate to? hello_nest [ Default ]
CREATE apps/hello_nest/src/hoge/hoge.middleware.spec.ts (180 bytes)
CREATE apps/hello_nest/src/hoge/hoge.middleware.ts (196 bytes)
hoge.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';

@Injectable()
export class HogeMiddleware implements NestMiddleware {
  use(req: any, res: any, next: () => void) {
    next();
  }
}

module

関連するコントローラーやサービスをグループ化するためのモジュールを生成。

generateコマンド実行結果
$ nest g module hoge
? Which project would you like to generate to? hello_nest [ Default ]
CREATE apps/hello_nest/src/hoge/hoge.module.ts (81 bytes)
UPDATE apps/hello_nest/src/app.module.ts (445 bytes) 
hoge.module.ts
import { Module } from '@nestjs/common';

@Module({})
export class HogeModule {}

pipe

リクエストのデータ変換やバリデーションを行うパイプを生成。

大文字に変換するpipeのサンプル↓

uppercase.pipe.ts
import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';

@Injectable()
export class UppercasePipe implements PipeTransform {
  transform(value: string, metadata: ArgumentMetadata): string {
    return value.toUpperCase();
  }
}
example.controller.ts
import { Controller, Get, Param } from '@nestjs/common';
import { UppercasePipe } from './pipes/uppercase.pipe';

@Controller('example')
export class ExampleController {
  @Get(':name')
  getGreeting(@Param('name', UppercasePipe) name: string) {
    return `Hello, ${name}!`;
  }
}
generateコマンド実行結果
$ nest g pipe hoge
? Which project would you like to generate to? hello_nest [ Default ]
CREATE apps/hello_nest/src/hoge/hoge.pipe.spec.ts (156 bytes)
CREATE apps/hello_nest/src/hoge/hoge.pipe.ts (220 bytes)
hoge.pipe.ts
import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common';

@Injectable()
export class HogePipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    return value;
  }
}

provider

依存注入のためのサービス、ファクトリー、ヘルパーなどのプロバイダーを生成。
@Injectableデコレータのついたクラスを生成して、moduleに登録するところまで。
あとは使いたいControllerのconstructorでDIして使えるようになる。

generateコマンド実行結果
$ nest g provider hoge
? Which project would you like to generate to? hello_nest [ Default ]
CREATE apps/hello_nest/src/hoge/hoge.spec.ts (399 bytes)
CREATE apps/hello_nest/src/hoge/hoge.ts (81 bytes)
UPDATE apps/hello_nest/src/hoge/hoge.module.ts (133 bytes) 
hoge.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class Hoge {}

resolver

GraphQL APIを構築する際に使用する。
GraphQLクエリやミューテーションを処理するリゾルバー雛形を生成。

hoge.resolver.ts
import { Query, Resolver } from '@nestjs/graphql';

@Resolver()
export class HogeResolver {
  @Query(() => String)
  hello(): string {
    return 'Hello World!';
  }
}
generateコマンド実行結果
$ nest g resolver hoge
? Which project would you like to generate to? hello_nest [ Default ]
CREATE apps/hello_nest/src/hoge/hoge.resolver.spec.ts (456 bytes)
CREATE apps/hello_nest/src/hoge/hoge.resolver.ts (86 bytes)
UPDATE apps/hello_nest/src/hoge/hoge.module.ts (226 bytes) 
hoge.resolver.ts
import { Resolver } from '@nestjs/graphql';

@Resolver()
export class HogeResolver {}

resource

新しいCRUDリソースを生成する。基本的なCRUD操作を実装したモジュール、コントローラー、サービスを含む。
トランスポート層に何を使うか選択できる
(REST API, GraphQL(code first), GraphQL(schema first), Microservice(non-HTTP), WebSockets)

generateコマンド実行結果
$ nest g resource
? Which project would you like to generate to? hello_nest [ Default ]
? What name would you like to use for this resource (plural, e.g., "users")? users
? What transport layer do you use? REST API
? Would you like to generate CRUD entry points? Yes
CREATE apps/hello_nest/src/users/users.controller.spec.ts (566 bytes)
CREATE apps/hello_nest/src/users/users.controller.ts (894 bytes)
CREATE apps/hello_nest/src/users/users.module.ts (248 bytes)
CREATE apps/hello_nest/src/users/users.service.spec.ts (453 bytes)
CREATE apps/hello_nest/src/users/users.service.ts (609 bytes)
CREATE apps/hello_nest/src/users/dto/create-user.dto.ts (30 bytes)
CREATE apps/hello_nest/src/users/dto/update-user.dto.ts (169 bytes)
CREATE apps/hello_nest/src/users/entities/user.entity.ts (21 bytes)
UPDATE package.json (2173 bytes)
UPDATE apps/hello_nest/src/app.module.ts (510 bytes)
✔ Packages installed successfully.

service

ビジネスロジックを処理するサービスを生成。

generateコマンド実行結果
$ nest g service hoge
? Which project would you like to generate to? hello_nest [ Default ]
CREATE apps/hello_nest/src/hoge/hoge.service.spec.ts (446 bytes)
CREATE apps/hello_nest/src/hoge/hoge.service.ts (88 bytes)
UPDATE apps/hello_nest/src/hoge/hoge.module.ts (285 bytes)
hoge.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class HogeService {}

Options

オプション エイリアス 説明
--dry-run -d ファイルシステムへの変更を行わずに変更内容を表示。
--project -p 要素を追加するプロジェクトを指定。
--flat 要素用のフォルダを生成しないようにする。
--collection -c インストールされたnpmパッケージのパッケージ名を指定することでそのパッケージ内からSchematicsを検索して利用する。
--spec スペックファイルの生成(デフォルト)。
--no-spec スペックファイルを生成しないようにする。

最後に

nest generateコマンドについて、一通りみてみました。
これを機にあまり馴染みのなかったinterceptorguardなどの概要を知ることができたので、実装の幅が広がったような気がします。

ここまで読んでいただき、ありがとうございました!

10
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?