NestJSとは – バックエンド用フレームワークでチャットAPIを実装してみる

Table of Contents

NestJSとは?

NestJSは、効率的でスケーラブルなサーバーサイドアプリケーションを構築するための、プログレッシブなNode.jsフレームワークです。TypeScriptをベースにしており、クリーンで整理されたコードを書くための構造を提供します。NestJSは、ただのライブラリではなく、アプリケーション全体の骨組み(フレームワーク)を提供することで、大規模なプロジェクトでも一貫性を保ち、開発を効率化することができます。本家の情報は、こちらを参照してください。

NestJSの主な特徴

1. TypeScriptファースト

NestJSはTypeScriptを完全にサポートしています。これにより、静的型付け、インターフェース、デコレーターといったTypeScriptの強力な機能を利用でき、エラーを早期に発見し、保守しやすいコードを書くことができます。

2. モジュール化されたアーキテクチャ

アプリケーションを再利用可能なモジュールに分割して構築します。これにより、コードの管理がしやすくなり、チーム開発や大規模なプロジェクトでも一貫性を保つことができます。

3. Express.jsの上に構築されている

NestJSは、Node.jsの最も一般的なフレームワークであるExpress.jsを内部で利用しています(設定でFastifyも選択可能)。これにより、Express.jsの豊富なミドルウェアやライブラリのエコシステムを活用しながら、より構造化された開発を進めることができます。

4. 依存性注入(Dependency Injection)

NestJSは、Angularと同様に依存性注入の仕組みを採用しています。これにより、コンポーネント間の結合度を低く保ち、テストがしやすく、拡張性の高いアプリケーションを構築できます。

5. CLI(コマンドラインインターフェース)

強力なCLIツールが用意されており、プロジェクトの作成や、モジュール、コントローラー、サービスの生成などを自動化してくれます。これにより、開発の初期設定にかかる時間を大幅に短縮できます。

NestJSを使うメリット

  • 大規模開発に適している: 規約に基づいた構造とモジュール化された設計により、大人数での開発や、長期にわたるプロジェクトのメンテナンスが容易になります。
  • 安定性と堅牢性: TypeScriptによる静的型付けと厳格なアーキテクチャのおかげで、バグが少なく、信頼性の高いアプリケーションを構築できます。
  • 学習コストは高めだが、一度習得すれば生産性が向上: Express.jsのような自由度の高いフレームワークに比べると、NestJSの規約を学ぶには少し時間がかかります。しかし、一度習得すれば、繰り返し行う作業が減り、より効率的に開発を進められます。

まとめると、NestJSは、シンプルなAPIサーバーから、マイクロサービスやリアルタイムアプリケーションまで、あらゆるサーバーサイドプロジェクトに構造と秩序をもたらす、非常に強力なフレームワークです。Node.jsで本格的なエンタープライズアプリケーションを開発したい場合に最適な選択肢と言えるでしょう。

名前が似ているNext.jsとの違い

NestJSNext.js
主な役割バックエンド(API・サーバー処理)フロントエンド(UI・SSR)
ベースNode.js + Express/FastifyReact
設計思想Angular風、DI & モジュール構造React風、ページルーティング中心
強み複雑なサーバーアプリ開発に強いSEO・UI・フロント実装に強い
API機能フル機能(REST, GraphQL, gRPCなど)簡易的(API Routes程度)
簡単に言うと:
NestJS = サーバーの脳みそ(ロジック担当)
Next.js = サイトの顔(見た目担当)

NestJSで作られるもの

1. REST APIサーバー

  • フロントエンド(Next.jsやReact、モバイルアプリ)からリクエストを受けてデータを返す。
  • 例:
    • ユーザー登録・ログインAPI
    • 商品一覧・注文API
    • 医療相談の履歴を返すAPI

2. GraphQL APIサーバー

  • クライアント側で柔軟にデータを取得したい場合に使われる。
  • ApolloやMercuriusと組み合わせるケースが多い。
  • 例:
    • Shopify風のAPI
    • ダッシュボードに必要なデータを一気に取得

3. 認証・認可システム

  • JWT, OAuth, Google/Shopifyログインなどを実装しやすい。
  • RBAC(Role Based Access Control)やACL(Access Control List)も管理できる。

4. マイクロサービス / メッセージング

  • KafkaやRabbitMQ、gRPCを使ったサービス間通信を構築できる。
  • 例:
    • 決済システムと在庫システムを分離して連携
    • 通知サービス(SMS/Email送信)

5. Webhooksや外部API連携

  • Shopify / Stripe / PayPal などから飛んでくるWebhookを受け取って処理。
  • 外部APIにアクセスして結果をDBに保存。

6. バックエンドの業務ロジック

  • 予約管理、在庫管理、注文処理、レポート生成など。
  • フロントではなく裏側で動く「システムの心臓部」を担当。

実際の利用例

  • 企業の基幹APIサーバー(金融、医療、EC)
  • B2B向けプラットフォームのバックエンド
  • IoTデバイスとの連携サーバー
  • AI/MLサービスのAPI化(モデル呼び出しをNestJSで管理)

まとめ

NestJSは 「サーバーサイドの本格的な基盤を作る」 ために使われるフレームワークです。
シンプルなAPIならExpressやNext.jsのAPI Routesで十分ですが、
大規模化・複雑化するならNestJSが最適

なぜ、Expressが利用されているのか?

Expressとは?

  • Node.js上で動く最小限のWebフレームワーク
  • HTTPリクエストを処理してレスポンスを返す仕組みを、とてもシンプルに実装できる
  • app.get('/', ...) みたいに直感的にルーティングが書ける

Expressがクロスプラットフォームでよく使われていた理由は?

1. Node.js自体がクロスプラットフォーム

  • Node.jsは Linux / macOS / Windows で同じコードが動く
  • その上に乗るExpressも当然クロスプラットフォームで動く

「Windowsで開発 → Linuxサーバーにデプロイ」も簡単。

2. 軽量 & シンプル

  • Expressは「最低限の機能だけ」を持つ
  • プラグインを組み合わせて自由に設計できるので、
    Web API, SPAのバックエンド, IoTデバイス制御, クロスプラットフォームアプリのバックエンド まで幅広く使われた

3. Electronやモバイルと相性がいい

  • Electronアプリ(Slack, VSCodeなど)
    → フロントはHTML/JS、裏でExpressを仕込んでAPIサーバー化する例が多い
  • モバイルアプリ(React Nativeなど)
    → バックエンドをExpressで作って通信するのが定番だった

4. 圧倒的な普及と学習コストの低さ

  • 世界中で使われているため、サンプルやライブラリが豊富
  • 他言語の人(Java, PHP, Python)でもとっつきやすく、学習コストが低い
  • だから「クロスプラットフォームアプリ開発の裏側にはExpressがよくある」という状況になった

まとめ

Expressは「クロスプラットフォームで動くNode.jsを、シンプルにWebサーバー化する定番ツール」として使われてきたから、いろんな文脈で登場するんです。
NestJSも実は内部的に デフォルトはExpressを使っている(必要ならFastifyに差し替え可能)。
つまりNestJSは「Expressの上に、Angular風の構造やDIを追加して、チーム開発しやすくした進化版」なんです。

Fastifyを使う理由は?

Fastifyとは?

  • Node.js向けの高速Webフレームワーク
  • Expressと同じように「ルーティングしてリクエストを処理する」役割
  • ただし 速度・拡張性・型安全性 に重点を置いている

キャッチコピーは「Fast and low overhead web framework for Node.js

ExpressとFastifyの違い

項目ExpressFastify
リリース年2010年2017年
性能普通高速(Expressの2〜3倍)
プラグイン豊富(歴史が長い)徐々に増えてきている
型サポート弱め(JSベース)TypeScript対応が強い
エコシステム世界中で定番近年人気上昇中
設計思想シンプルJSON Schemaベースで厳格

NestJSとFastify

NestJSはデフォルトで Expressベース ですが、設定を変えると Fastifyベース に切り替えられます。

const app = await NestFactory.create<NestFastifyApplication>(
  AppModule,
  new FastifyAdapter(),
);

Fastifyに変える理由

  1. パフォーマンスが必要
    • 高トラフィックなAPIサーバー(広告配信、IoT、大規模ECなど)ではExpressより速い
  2. スケーラビリティ
    • JSON Schemaによるバリデーションがビルトイン → 大規模でも安定
  3. 型安全性
    • TypeScriptプロジェクトとの相性が良い

まとめ

  • Express → 世界中で定番、ライブラリ豊富、学習コスト低い
  • Fastify → 新しめ、速い、大規模/高トラフィックに強い、TSとの親和性高い

プロジェクトを作成してみる

プロジェクト作成

$ npx @nestjs/cli new my-project

✨  We will scaffold your app in a few seconds..

✔ Which package manager would you ❤️  to use? pnpm
CREATE my-project/.prettierrc (51 bytes)
CREATE my-project/README.md (5036 bytes)
CREATE my-project/eslint.config.mjs (836 bytes)
CREATE my-project/nest-cli.json (171 bytes)
CREATE my-project/package.json (1988 bytes)
CREATE my-project/tsconfig.build.json (97 bytes)
CREATE my-project/tsconfig.json (677 bytes)
CREATE my-project/src/app.controller.ts (274 bytes)
CREATE my-project/src/app.module.ts (249 bytes)
CREATE my-project/src/app.service.ts (142 bytes)
CREATE my-project/src/main.ts (228 bytes)
CREATE my-project/src/app.controller.spec.ts (617 bytes)
CREATE my-project/test/jest-e2e.json (183 bytes)
CREATE my-project/test/app.e2e-spec.ts (674 bytes)

✔ Installation in progress... ☕

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

動作確認

$ cd my-project
$ pnpm run start

> my-project@0.0.1 start /User/xxx/dev/my-project
> nest start

[Nest] 49220  - 09/07/2025, 4:47:56 PM     LOG [NestFactory] Starting Nest application...
[Nest] 49220  - 09/07/2025, 4:47:56 PM     LOG [InstanceLoader] AppModule dependencies initialized +6ms
[Nest] 49220  - 09/07/2025, 4:47:56 PM     LOG [RoutesResolver] AppController {/}: +1ms
[Nest] 49220  - 09/07/2025, 4:47:56 PM     LOG [RouterExplorer] Mapped {/, GET} route +1ms
[Nest] 49220  - 09/07/2025, 4:47:56 PM     LOG [NestApplication] Nest application successfully started +1ms
% curl http://localhost:3000
Hello World!%

初期フォルダ構成

./pnpm-lock.yaml
./test
./test/app.e2e-spec.ts
./test/jest-e2e.json
./nest-cli.json
./README.md
./package.json
./tsconfig.build.json
./.prettierrc
./tsconfig.json
./eslint.config.mjs
./src
./src/main.ts
./src/app.service.ts
./src/app.module.ts
./src/app.controller.spec.ts
./src/app.controller.ts

複数アプリを管理しやすいようにmonorepo化

apiアプリ作成

REST APIサーバー用

% npx nest g app api

フォルダ構成の変化

  • appsフォルダが追加される
  • apps/my-projectフォルダが追加される
  • apps/my-project/以下にtest, src, tsconfig.app.jsonが移動される
  • apps/apiフォルダが作成され、新しいアプリが追加される
./pnpm-lock.yaml
./nest-cli.json
./README.md
./package.json
./tsconfig.build.json
./.prettierrc
./tsconfig.json
./apps
./apps/my-project
./apps/my-project/tsconfig.app.json
./apps/my-project/test
./apps/my-project/test/app.e2e-spec.ts
./apps/my-project/test/jest-e2e.json
./apps/my-project/src
./apps/my-project/src/main.ts
./apps/my-project/src/app.service.ts
./apps/my-project/src/app.module.ts
./apps/my-project/src/app.controller.spec.ts
./apps/my-project/src/app.controller.ts
./apps/api
./apps/api/tsconfig.app.json
./apps/api/test
./apps/api/test/app.e2e-spec.ts
./apps/api/test/jest-e2e.json
./apps/api/src
./apps/api/src/api.service.ts
./apps/api/src/api.module.ts
./apps/api/src/main.ts
./apps/api/src/api.controller.ts
./apps/api/src/api.controller.spec.ts
./eslint.config.mjs

workerアプリ作成

非同期処理、バックグラウンドのバッチなど

% npx nest g app worker

ライブラリ 作成

ライブラリは共通して利用するためcommon, database, utils等の追加が考えられる

% npx nest g lib common
✔ What prefix would you like to use for the library (default: @app or 'defaultLibraryPrefix' setting value)? 
CREATE libs/common/tsconfig.lib.json (220 bytes)
CREATE libs/common/src/index.ts (67 bytes)
CREATE libs/common/src/common.module.ts (192 bytes)
CREATE libs/common/src/common.service.spec.ts (460 bytes)
CREATE libs/common/src/common.service.ts (90 bytes)
UPDATE nest-cli.json (1335 bytes)
UPDATE package.json (2195 bytes)
UPDATE tsconfig.json (817 bytes)

% npx nest g lib database
✔ What prefix would you like to use for the library (default: @app or 'defaultLibraryPrefix' setting value)? 
CREATE libs/database/tsconfig.lib.json (222 bytes)
CREATE libs/database/src/index.ts (71 bytes)
CREATE libs/database/src/database.module.ts (202 bytes)
CREATE libs/database/src/database.service.spec.ts (474 bytes)
CREATE libs/database/src/database.service.ts (92 bytes)
UPDATE nest-cli.json (1578 bytes)
UPDATE package.json (2260 bytes)
UPDATE tsconfig.json (945 bytes)

nest-cli.jsonの確認

{
  "$schema": "https://json.schemastore.org/nest-cli",
  "collection": "@nestjs/schematics",
  "sourceRoot": "apps/my-project/src",
  "compilerOptions": {
    "deleteOutDir": true,
    "webpack": true,
    "tsConfigPath": "apps/my-project/tsconfig.app.json"
  },
  "monorepo": true,
  "root": "apps/my-project",
  "projects": {
    "ai-avatar-backend": {
      "type": "application",
      "root": "apps/my-project",
      "entryFile": "main",
      "sourceRoot": "apps/my-project/src",
      "compilerOptions": {
        "tsConfigPath": "apps/my-project/tsconfig.app.json"
      }
    },
    "api": {
      "type": "application",
      "root": "apps/api",
      "entryFile": "main",
      "sourceRoot": "apps/api/src",
      "compilerOptions": {
        "tsConfigPath": "apps/api/tsconfig.app.json"
      }
    },
    "common": {
      "type": "library",
      "root": "libs/common",
      "entryFile": "index",
      "sourceRoot": "libs/common/src",
      "compilerOptions": {
        "tsConfigPath": "libs/common/tsconfig.lib.json"
      }
    },
    "database": {
      "type": "library",
      "root": "libs/database",
      "entryFile": "index",
      "sourceRoot": "libs/database/src",
      "compilerOptions": {
        "tsConfigPath": "libs/database/tsconfig.lib.json"
      }
    },
    "worker": {
      "type": "application",
      "root": "apps/worker",
      "entryFile": "main",
      "sourceRoot": "apps/worker/src",
      "compilerOptions": {
        "tsConfigPath": "apps/worker/tsconfig.app.json"
      }
    }
  }
}

依存の追加 (openai)

pnpm add openai @nestjs/config

環境変数 apps/api/.env設定

OPENAI_API_KEY=sk-xxxxxx
PORT=3000

./apps/api/src/api.module.ts 変更

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { ChatModule } from './chat/chat.module';

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    ChatModule,
  ],
})
export class ApiModule {}

./apps/api/src/chat/chat.module.ts 追加

  • @Moduleデコレーターで、コントローラー(ChatController)とプロバイダー(ChatService)を登録しています。
  • これにより、チャット機能に関するAPIエンドポイントやビジネスロジックが、このモジュール内で管理されます。
  • ChatModuleは、アプリケーションのチャット機能をまとめる役割を持っています。
import { Module } from '@nestjs/common';
import { ChatService } from './chat.service';
import { ChatController } from './chat.controller';

@Module({
  controllers: [ChatController],
  providers: [ChatService],
})
export class ChatModule {}

./apps/api/src/chat/chat.service.ts 追加

ChatServiceで、OpenAIのAPIを使ってチャットの応答を生成します。

  • @Injectable() デコレーターでサービスとして定義。
  • コンストラクタでOpenAIクライアントを初期化。
  • getReply(message: string): Promise<string> という非同期メソッドで、ユーザーからのメッセージを受け取り、OpenAIのgpt-4o-miniモデルに問い合わせて応答を生成。
  • システムプロンプト(assistantの役割説明)とユーザーメッセージをAPIに渡し、返ってきた応答(completion.choices[0].message.content)を返す。
import { Injectable } from '@nestjs/common';
import OpenAI from 'openai';

@Injectable()
export class ChatService {
  private client: OpenAI;

  constructor() {
    this.client = new OpenAI({
      apiKey: process.env.OPENAI_API_KEY,
    });
  }

  async getReply(message: string): Promise<string> {
    const completion = await this.client.chat.completions.create({
      model: 'gpt-4o-mini',
      messages: [
        { role: 'system', content: 'You are a helpful care coordinator assistant.' },
        { role: 'user', content: message },
      ],
    });

    return completion.choices[0].message.content ?? '';
  }
}

apps/api/src/chat/chat.controller.ts 追加

ChatControllerで、チャットAPIのエンドポイントを定義しています。

  • @Controller(‘chat’) で、/chat パスへのリクエストを受け付けるコントローラーを定義。
  • コンストラクタでChatServiceを注入し、サービスの機能を利用可能に。
  • @Post() デコレーターで、POSTリクエスト(/chat)を受け付けるchatメソッドを定義。
  • リクエストボディのmessageプロパティを受け取り、未入力ならエラーを返す。
  • messageがあれば、chatService.getReply(message)でOpenAIの応答を取得し、{ reply } 形式で返す。
import { Controller, Post, Body } from '@nestjs/common';
import { ChatService } from './chat.service';

@Controller('chat')
export class ChatController {
  constructor(private readonly chatService: ChatService) {}

  @Post()
  async chat(@Body('message') message: string) {
    if (!message) {
      return { error: 'message is required' };
    }
    const reply = await this.chatService.getReply(message);
    return { reply };
  }
}

動作確認

% pnpm start api

% curl -X POST http://localhost:3000/chat \
  -H "Content-Type: application/json" \
  -d '{"message":"Hello, can you help me find hair loss products?"}'

{"reply":"Sure! There are various types of hair loss products available, 
including topical treatments, dietary supplements, and devices. 
Here are some popular categories and examples:\n\n1. ... "}

関連記事

カテゴリー

アーカイブ

Lang »