NestJSを理解しよう
NestJSとは何か?
Nest.jsは、効率的でスケーラブルなサーバーサイドアプリケーションを構築するためのNode.jsのフレームワークです。TypeScriptを使用し、オブジェクト指向プログラミング、関数型プログラミング、そしてリアクティブプログラミングの要素を組み合わせています。
NEstJSの主な特徴
- TypeScriptベース: Nest.jsはTypeScriptで書かれており、堅牢なアプリケーションの開発を支援します。
- モジュラー構造: 疎結合で再利用可能なモジュールにより、大規模アプリケーションの管理が容易になります。
- DI(依存性注入)システム: 依存関係の管理がシンプルで、テストや保守が容易になります。
- マイクロサービス対応: マイクロサービスアーキテクチャをサポートしており、柔軟なシステム設計が可能です
NestJSの基本構成要素
コントローラー
NestJSでは、コントローラーは基本的なリクエストハンドラです。
コントローラーはクライアントのリクエストを受け取り、それに対するレスポンスを返す役割を担います。ルートハンドラとして機能し、特定のパスへのリクエストに応じて処理を実行します。
import { Controller, Get } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get()
findAll() {
return 'This action returns all cats';
}
}
このコードは、’cats’というエンドポイントにGETリクエストが来たときに、’This action returns all cats’というメッセージを返します。
プロバイダー
プロバイダーはNestJSの基本的な概念であり、サービス、リポジトリ、ファクトリーなどがこれに含まれます。依存性注入を通じて他のクラスに機能を提供します。
import { Injectable } from '@nestjs/common';
@Injectable()
export class CatsService {
private readonly cats: Cat[] = [];
create(cat: Cat) {
this.cats.push(cat);
}
findAll(): Cat[] {
return this.cats;
}
}
このサービスは、猫のデータを管理します。
新しい猫を作成するメソッドと、すべての猫を取得するメソッドがあります。
モジュール
モジュールはアプリケーションの一部分を構成し、コントローラーやプロバイダーをまとめる容器の役割を果たします。アプリケーションの構造を整理し、機能ごとに分割することができます。
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule {}
このモジュールは、CatsControllerとCatsServiceを一緒にグループ化します。
ミドルウェア
NestJSのミドルウェアは、特定のルートハンドラが呼び出される前後に実行される関数です。
これにより、リクエストとレスポンスオブジェクトを操作したり、次のミドルウェア関数を呼び出したりすることができます。
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log('Request...');
next();
}
}
このミドルウェアは、リクエストが来るたびに”Request…”とログに出力します。
例外フィルター
NestJSの例外フィルターは、特定のルートハンドラで発生した例外を処理するためのものです。
これにより、例外の詳細をログに記録したり、クライアントにカスタムレスポンスを送信したりすることができます。
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
この例外フィルターは、HttpExceptionをキャッチし、カスタムエラーレスポンスをクライアントに送信します。
パイプ
NestJSのパイプは、メソッドが呼び出される前に引数を変換、検証、または操作するためのものです。
これにより、コントローラーが正しいデータ形式で動作することを保証できます。
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
@Injectable()
export class ValidationPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
if (!value) {
throw new BadRequestException('Value not provided');
}
return value;
}
}
このパイプは、値が提供されていない場合にBadRequestExceptionをスローします。
ガード
NestJSのガードは、特定のルートハンドラが実行される前に特定の条件を満たしているかどうかを確認するためのものです。
これにより、認証や認可などのセキュリティ要件を満たすことができます。
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 validateRequest(request);
}
}
このガードは、リクエストがvalidateRequest関数によって検証されることを要求します。
インターセプター
NestJSのインターセプターは、メソッドの実行前後に追加のロジックをバインドするためのものです。
これにより、パフォーマンスのロギング、レスポンスの変換、例外の変換などを行うことができます。
import { Injectable, 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> {
console.log('Before...');
const now = Date.now();
return next
.handle()
.pipe(
tap(() => console.log(`After... ${Date.now() - now}ms`)),
);
}
}
このインターセプターは、メソッドの実行前後にログを出力します。
カスタムデコレータ
NestJSでは、カスタムデコレータを作成することで、コードの再利用性と可読性を向上させることができます。
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const User = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);
このデコレータは、リクエストオブジェクトからユーザー情報を抽出します。
環境設定とインストール
必要なツールと環境
Nest.jsを使用するためには、Node.jsとnpm(またはYarn)が必要です。Node.jsはJavaScriptをサーバーサイドで実行するための環境であり、npmやYarnは依存関係の管理とパッケージのインストールを行うツールです。
Nest.jsのインストール方法
Nest.jsのインストールは非常に簡単です。以下のコマンドを実行することで、Nest CLI(コマンドラインインターフェース)をインストールできます。
npm i -g @nestjs/cli
これにより、Nest.jsプロジェクトの生成や管理を行うことが可能になります。
最初のプロジェクトの作成
プロジェクトの生成
Nest.jsプロジェクトの生成は、Nest CLIを使用して行います。以下のコマンドで新しいプロジェクトを生成できます。
nest new project-name
ここでproject-name
はプロジェクトの名前に置き換えてください。
基本的なファイル構成
生成されたプロジェクトには、以下のような基本的なファイル構成が含まれます。
src
フォルダ: アプリケーションの主要なコードが含まれます。main.ts
: アプリケーションのエントリポイント。app.module.ts
: アプリケーションのルートモジュール。app.controller.ts
: 基本的なコントローラー。app.service.ts
: 基本的なサービス。
これらのファイルは、Nest.jsアプリケーションの基礎を構成し、開発の出発点となります。
より詳しく解説!
ルーティングの基本
ルーティングは、特定のパスへのリクエストを適切なコントローラーに割り当てるプロセスです。Nest.jsではデコレータを使用してルートを定義します。たとえば、@Get()
デコレータはHTTP GETリクエストを指定のメソッドに割り当てます。
コントローラーの作成と構成
コントローラーは、アプリケーションの特定の部分を処理するクラスです。以下は簡単なコントローラーの例です。
@Controller('users')
export class UsersController {
@Get()
findAll(): string {
return 'This action returns all users';
}
}
ここでは、@Controller('users')
デコレータを使用して、このコントローラーが/users
ルートを処理することを指定しています。
サービスとプロバイダー
サービスの役割
サービスはビジネスロジックをカプセル化し、異なるコントローラー間で再利用可能なコードを提供します。サービスはプロバイダーとしてNest.jsフレームワークに登録され、依存性注入を通じてコントローラーに注入されます。
プロバイダーの定義と使用
プロバイダーは依存性注入を通じて他のクラスにサービスを提供するクラスです。以下は簡単なサービス(プロバイダー)の例です。
@Injectable()
export class UsersService {
findAll(): string[] {
return ['user1', 'user2'];
}
}
データモデルとDTO(Data Transfer Object)
データモデルの作成
Nest.jsでデータモデルを作成する際には、TypeScriptのクラスを使用します。これにより、アプリケーション内で扱うデータの構造を明確に定義できます。以下は簡単なユーザーモデルの例です。
export class User {
id: number;
name: string;
email: string;
}
DTOの導入と利点
DTO(Data Transfer Object)は、異なるシステムやアプリケーション層間でデータを転送するためのオブジェクトです。DTOを使用することで、入力データの検証や変換を簡単に行えます。
データベースの統合
データベースとの接続
Nest.jsはTypeORMやMongooseなど、複数のORM(Object-Relational Mapping)をサポートしています。これにより、データベースとの接続や操作が容易になります。
ORMの利用
ORMを使用することで、データベーステーブルとモデルクラスをマッピングし、SQLクエリを直接書くことなくデータベース操作を行うことができます。以下はTypeORMを使用したユーザーモデルの例です。
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
email: string;
}
エラー処理とセキュリティ
エラーハンドリング
Nest.jsでは、例外フィルタを使用してアプリケーション全体のエラー処理をカスタマイズできます。これにより、エラーレスポンスのフォーマットや例外のロギングを統一的に管理することが可能です。
基本的なセキュリティ対策
セキュリティはWebアプリケーションにおいて重要な要素です。Nest.jsでは、HelmetやCORSの設定、Rate Limitingなどのミドルウェアを使用して基本的なセキュリティ対策を行うことができます。
結論と次のステップ
学んだことのまとめ
このブログ記事では、Nest.jsの基本概念、環境設定、プロジェクトの作成方法、ルーティングとコントローラー、サービスとプロバイダー、データモデルとDTO、データベースの統合、エラー処理とセキュリティについて学びました。
Nest.jsでのさらなる学習リソース
Nest.jsの学習を深めるためには、公式ドキュメントを読むこと、実際にプロジェクトを作成してみること、コミュニティに参加して経験を共有することが有効です。