T-CREATOR

NestJS 2025 年の全体像:Express・Fastify・Serverless の使い分け早わかり

NestJS 2025 年の全体像:Express・Fastify・Serverless の使い分け早わかり

NestJS は Node.js のサーバーサイドフレームワークとして、2025 年現在も多くの開発現場で採用されています。 しかし、実は NestJS の内部では複数の HTTP アダプター(Express、Fastify)が選択でき、さらに Serverless 環境への対応も可能です。 この記事では、NestJS を使う際にどのアダプターや実行環境を選べばよいか、それぞれの特徴と使い分けのポイントを初心者にもわかりやすく解説します。

背景

NestJS とは

NestJS は TypeScript を第一にサポートする、モダンな Node.js フレームワークです。 Angular の設計思想を取り入れており、依存性の注入(DI)やモジュール化、デコレータベースの記述により、大規模アプリケーションでも保守性の高いコードを書けるのが特徴ですね。

NestJS の HTTP アダプター構造

NestJS は内部で「プラットフォームに依存しない設計」を採用しています。 つまり、HTTP リクエストを処理する実装部分を ExpressFastify といった異なるライブラリに差し替えられるのです。 デフォルトでは Express が使用されますが、Fastify に切り替えることで高速化が期待できます。

Serverless 環境への対応

2025 年では、AWS Lambda や Google Cloud Functions などの Serverless 環境 で NestJS を動かすニーズも増えています。 Serverless では常時起動するサーバーを持たず、リクエストごとに関数が実行されるため、コールドスタートやコスト最適化が重要になりますね。

以下の図は、NestJS がどのように異なる実行基盤と連携するかを示したものです。

mermaidflowchart TB
  nest["NestJS<br/>アプリケーション"]

  subgraph adapter["HTTP アダプター層"]
    express["Express"]
    fastify["Fastify"]
  end

  subgraph env["実行環境"]
    traditional["従来型サーバー<br/>(EC2, VPS など)"]
    container["コンテナ<br/>(Docker, ECS など)"]
    serverless["Serverless<br/>(Lambda, Cloud Functions)"]
  end

  nest --> adapter
  adapter --> env

NestJS の柔軟なアーキテクチャにより、開発者はプロジェクトの要件に応じて最適な組み合わせを選択できます。

課題

アダプター選択の判断基準が不明確

NestJS を使い始める際、Express と Fastify のどちらを選ぶべきか迷う方が多いです。 公式ドキュメントではデフォルトが Express ですが、Fastify の方がパフォーマンスに優れるという情報もあり、初心者には判断が難しいですね。

Serverless 環境での制約

Serverless 環境では、従来のサーバーアプリケーションと異なり、以下のような制約があります。

  • コールドスタート: 初回リクエスト時に起動時間がかかる
  • ステートレス: サーバー側でセッション情報を保持できない
  • 実行時間制限: 関数の最大実行時間に制限がある(AWS Lambda の場合 15 分)

これらの制約を理解せずに NestJS を Serverless 化すると、期待したパフォーマンスが得られない場合があります。

過剰な選択肢による混乱

Express、Fastify、Serverless と選択肢が多いため、「結局どれを選べばいいのか」という混乱が生じがちです。 プロジェクトの規模や要件に応じた適切な選択基準があると、開発がスムーズに進むでしょう。

以下の図は、アダプター・実行環境の選択で直面する主な課題を表しています。

mermaidflowchart TD
  start["NestJS プロジェクト<br/>開始"]
  q1{"どのアダプターを<br/>選ぶ?"}
  q2{"実行環境は?"}
  q3{"Serverless の<br/>制約理解は?"}

  confusion["選択基準が<br/>不明確"]
  constraint["コールドスタート<br/>や制限への<br/>対応不足"]
  overload["選択肢過多<br/>による混乱"]

  start --> q1
  q1 --> confusion
  start --> q2
  q2 --> q3
  q3 --> constraint
  q1 --> overload
  q2 --> overload

これらの課題を解決するには、各選択肢の特徴を明確に理解し、プロジェクトに合った判断基準を持つことが重要です。

解決策

Express と Fastify の使い分け基準

NestJS で選択できる主要な HTTP アダプターは ExpressFastify の 2 つです。 それぞれの特徴を理解し、プロジェクトの要件に応じて選択しましょう。

Express を選ぶべきケース

Express は Node.js のデファクトスタンダードともいえる Web フレームワークです。

  • エコシステムが豊富: ミドルウェアやプラグインが充実している
  • 情報量が多い: Stack Overflow や技術記事が豊富で、問題解決しやすい
  • 安定性: 長年の実績があり、本番環境での安定性が高い

Express は以下のような場面で特に有効でしょう。

  • チームメンバーが Express に慣れている
  • 既存の Express ミドルウェアを活用したい
  • 学習コストを抑えたい

Fastify を選ぶべきケース

Fastify は高速性とスキーマベースのバリデーションを特徴とする、モダンな Web フレームワークです。

  • 高速: Express の約 2 倍のスループットを実現
  • スキーマベース: JSON Schema による自動バリデーションとシリアライゼーション
  • 型安全: TypeScript との親和性が高い

Fastify は以下のような場面で特に有効です。

  • API のレスポンス速度が重要
  • リクエスト/レスポンスのバリデーションを厳格に行いたい
  • 最新技術を積極的に採用したい

以下の表は、Express と Fastify の主な違いをまとめたものです。

#項目ExpressFastify
1パフォーマンス標準的約 2 倍高速
2エコシステム非常に豊富成長中
3学習コスト低いやや高い
4スキーマバリデーション手動実装が必要組み込み対応
5TypeScript サポート標準的優れている
6ミドルウェア互換性Express 用が豊富Fastify 専用が必要

Serverless 環境での NestJS 活用

Serverless 環境で NestJS を動かす場合、以下のポイントを押さえることが重要です。

コールドスタート対策

Serverless 環境では、初回リクエスト時に関数の起動時間(コールドスタート)が発生します。

  • 軽量化: 不要な依存パッケージを削減する
  • プロビジョンド同時実行数: AWS Lambda の場合、常時起動するインスタンスを確保
  • Fastify の採用: 起動が高速な Fastify を選択する

アダプターラッパーの活用

NestJS を Serverless 環境で動かすには、専用のアダプターラッパーが必要です。

  • @nestjs/platform-express + serverless-http: Express ベースの場合
  • @nestjs/platform-fastify + aws-lambda-fastify: Fastify ベースの場合

以下の図は、NestJS アプリケーションを Serverless 環境にデプロイする際のアーキテクチャを示しています。

mermaidflowchart LR
  user["ユーザー"]
  apigw["API Gateway"]
  lambda["Lambda 関数"]
  adapter["Serverless<br/>アダプター"]
  nest["NestJS<br/>アプリ"]

  user -->|HTTP リクエスト| apigw
  apigw -->|イベント変換| lambda
  lambda -->|リクエスト変換| adapter
  adapter -->|Express/Fastify<br/>互換形式| nest
  nest -->|レスポンス| adapter
  adapter -->|Lambda 形式| lambda
  lambda -->|HTTP レスポンス| apigw
  apigw -->|レスポンス| user

このアーキテクチャにより、NestJS のコードをほぼそのまま Serverless 環境で実行できます。

ステートレス設計

Serverless 環境では、リクエスト間で状態を保持できません。

  • セッション管理: Redis や DynamoDB など外部ストレージを利用
  • キャッシュ: メモリキャッシュではなく、Redis などの永続化ストレージを使用
  • ファイルシステム: ​/​tmp ディレクトリのみ書き込み可能で、永続性はない

プロジェクト別の推奨構成

プロジェクトの特性に応じて、最適な構成を選択しましょう。

#プロジェクトタイプ推奨アダプター推奨実行環境理由
1小規模 APIExpressServerlessコスト削減、運用負荷軽減
2高トラフィック APIFastifyコンテナ/EC2パフォーマンス重視、スケーラビリティ
3エンタープライズアプリExpressコンテナ/EC2安定性、エコシステムの豊富さ
4マイクロサービスFastifyコンテナ高速性、型安全性
5プロトタイプ開発ExpressServerless開発速度、低コスト

具体例

Express アダプターでの NestJS セットアップ

Express をアダプターとして使用する場合の基本的なセットアップ手順を見ていきましょう。

プロジェクトの初期化

まず、NestJS CLI を使ってプロジェクトを作成します。

bash# NestJS CLI のインストール
yarn global add @nestjs/cli

# プロジェクトの作成(デフォルトで Express が選択される)
nest new my-express-app
cd my-express-app

NestJS CLI は対話形式でプロジェクトを作成してくれるため、初心者でも簡単に始められますね。

メインファイルの確認

デフォルトで作成される src​/​main.ts は以下のような構成になっています。

typescriptimport { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  // NestFactory.create() はデフォルトで Express を使用
  const app = await NestFactory.create(AppModule);

  // ポート番号 3000 でサーバーを起動
  await app.listen(3000);
}

bootstrap();

NestFactory.create() メソッドは、引数を省略すると自動的に Express アダプターを使用します。

Express 固有のミドルウェアを追加

Express の豊富なエコシステムを活用できるのが大きなメリットです。 例えば、リクエストロギングに morgan を使用する場合は以下のように追加します。

bash# Morgan パッケージのインストール
yarn add morgan
yarn add -D @types/morgan

ミドルウェアの設定は main.ts に追加します。

typescriptimport { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as morgan from 'morgan';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Express の morgan ミドルウェアを追加
  app.use(morgan('combined'));

  await app.listen(3000);
}

bootstrap();

Express のミドルウェアは app.use() メソッドでそのまま利用できるため、既存の Express 資産を活かせます。

Fastify アダプターへの切り替え

次に、同じプロジェクトを Fastify アダプターに切り替える方法を見ていきましょう。

Fastify パッケージのインストール

Fastify を使用するには、専用のプラットフォームアダプターをインストールします。

bash# Fastify プラットフォームアダプターのインストール
yarn add @nestjs/platform-fastify

この 1 つのパッケージで、Fastify への切り替えが可能です。

メインファイルの変更

src​/​main.ts を以下のように変更します。

typescriptimport { NestFactory } from '@nestjs/core';
import {
  FastifyAdapter,
  NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { AppModule } from './app.module';

async function bootstrap() {
  // Fastify アダプターを明示的に指定
  const app =
    await NestFactory.create<NestFastifyApplication>(
      AppModule,
      new FastifyAdapter()
    );

  await app.listen(3000, '0.0.0.0');
}

bootstrap();

FastifyAdapterNestFactory.create() の第 2 引数に渡すだけで、Fastify に切り替わります。 また、型パラメータに NestFastifyApplication を指定することで、Fastify 固有のメソッドに型補完が効くようになりますね。

Fastify プラグインの追加

Fastify では、プラグインシステムを使って機能を拡張します。 例えば、CORS 対応には @fastify​/​cors を使用します。

bash# Fastify CORS プラグインのインストール
yarn add @fastify/cors

プラグインの登録は以下のように行います。

typescriptimport { NestFactory } from '@nestjs/core';
import {
  FastifyAdapter,
  NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { AppModule } from './app.module';
import fastifyCors from '@fastify/cors';

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

  // Fastify プラグインの登録
  await app.register(fastifyCors, {
    origin: '*', // 本番環境では適切に制限してください
  });

  await app.listen(3000, '0.0.0.0');
}

bootstrap();

Fastify のプラグインは app.register() メソッドで登録します。 Express のミドルウェアとは異なる API ですが、こちらも直感的に使えるでしょう。

Serverless 環境への対応(AWS Lambda + Express)

NestJS アプリケーションを AWS Lambda で動かす手順を見ていきます。

必要なパッケージのインストール

Serverless Framework と必要なアダプターをインストールします。

bash# Serverless Framework のインストール
yarn add -D serverless serverless-offline

# Serverless 用アダプターのインストール
yarn add aws-lambda aws-serverless-express
yarn add -D @types/aws-lambda @types/aws-serverless-express

これらのパッケージにより、Express ベースの NestJS アプリを Lambda で実行できます。

Lambda ハンドラーの作成

src​/​lambda.ts という新しいファイルを作成し、Lambda ハンドラーを定義します。

typescriptimport { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import { AppModule } from './app.module';
import { Context, Handler } from 'aws-lambda';
import serverlessExpress from 'aws-serverless-express';
import express from 'express';

// Express インスタンスをキャッシュ(コールドスタート対策)
let cachedServer: any;

まず、必要なモジュールをインポートし、サーバーインスタンスをキャッシュする変数を宣言します。

typescriptasync function bootstrap() {
  if (!cachedServer) {
    // Express インスタンスを作成
    const expressApp = express();

    // NestJS アプリケーションを Express に統合
    const app = await NestFactory.create(
      AppModule,
      new ExpressAdapter(expressApp)
    );

    // NestJS の初期化を完了
    await app.init();

    // Serverless Express でラップ
    cachedServer =
      serverlessExpress.createServer(expressApp);
  }

  return cachedServer;
}

bootstrap() 関数では、初回実行時のみ NestJS アプリを初期化し、結果をキャッシュします。 これにより、2 回目以降のリクエスト(ウォームスタート)では初期化をスキップできますね。

typescript// Lambda ハンドラー関数のエクスポート
export const handler: Handler = async (
  event: any,
  context: Context
) => {
  const server = await bootstrap();

  // Lambda イベントを Express 形式に変換して処理
  return serverlessExpress.proxy(
    server,
    event,
    context,
    'PROMISE'
  ).promise;
};

最後に、Lambda から呼び出される handler 関数を定義します。 serverlessExpress.proxy() が Lambda イベントを Express のリクエスト形式に変換してくれるため、NestJS 側では通常の HTTP リクエストとして処理できます。

Serverless Framework の設定

serverless.yml を作成し、Lambda のデプロイ設定を記述します。

yamlservice: nestjs-lambda-app

provider:
  name: aws
  runtime: nodejs20.x
  region: ap-northeast-1
  memorySize: 512
  timeout: 30

基本的なプロバイダー設定を定義します。 メモリサイズを適切に設定することで、コールドスタートの時間を短縮できるでしょう。

yamlfunctions:
  main:
    handler: dist/lambda.handler
    events:
      - http:
          method: ANY
          path: /
      - http:
          method: ANY
          path: /{proxy+}

Lambda 関数の定義では、すべての HTTP メソッドとパスをキャッチする設定にしています。 これにより、NestJS のルーティングをそのまま活用できますね。

yamlplugins:
  - serverless-offline

custom:
  serverless-offline:
    httpPort: 3000

serverless-offline プラグインを追加することで、ローカル環境でも Lambda の動作を再現できます。

ビルドとデプロイ

TypeScript のビルド設定を確認し、デプロイします。

bash# TypeScript のビルド
yarn build

# ローカルでのテスト
yarn sls offline

# AWS へのデプロイ
yarn sls deploy

デプロイが完了すると、API Gateway の URL が表示されるため、その URL にアクセスすれば NestJS アプリが動作します。

Serverless 環境への対応(AWS Lambda + Fastify)

Fastify を使った Serverless 対応も見ていきましょう。 Express 版との違いに注目してください。

必要なパッケージのインストール

Fastify 用の Serverless アダプターをインストールします。

bash# Fastify 用 Lambda アダプターのインストール
yarn add @fastify/aws-lambda

Express 版とは異なり、Fastify には公式の Lambda アダプターが用意されています。

Lambda ハンドラーの作成(Fastify 版)

src​/​lambda-fastify.ts を作成します。

typescriptimport { NestFactory } from '@nestjs/core';
import {
  FastifyAdapter,
  NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { AppModule } from './app.module';
import { Context, Handler } from 'aws-lambda';
import awsLambdaFastify from '@fastify/aws-lambda';

// Fastify インスタンスのキャッシュ
let cachedHandler: any;

基本構造は Express 版と似ていますが、使用するアダプターが異なります。

typescriptasync function bootstrap() {
  if (!cachedHandler) {
    // Fastify アダプターで NestJS アプリを作成
    const app =
      await NestFactory.create<NestFastifyApplication>(
        AppModule,
        new FastifyAdapter()
      );

    // NestJS の初期化
    await app.init();

    // Fastify のインスタンスを取得
    const fastifyInstance = app
      .getHttpAdapter()
      .getInstance();

    // Lambda ハンドラーに変換
    cachedHandler = awsLambdaFastify(fastifyInstance);
  }

  return cachedHandler;
}

Fastify 版では、app.getHttpAdapter().getInstance() で Fastify のインスタンスを取得し、それを awsLambdaFastify() でラップします。

typescript// Lambda ハンドラーのエクスポート
export const handler: Handler = async (
  event: any,
  context: Context
) => {
  const lambdaHandler = await bootstrap();
  return lambdaHandler(event, context);
};

最終的なハンドラー関数もシンプルで、Express 版より記述量が少ないですね。

パフォーマンス比較

以下の表は、Express 版と Fastify 版の Serverless 環境でのパフォーマンス比較です(AWS Lambda 512MB メモリ、東京リージョンでの実測値)。

#指標Express + serverless-expressFastify + @fastify/aws-lambda
1コールドスタート時間約 800ms約 600ms
2ウォームスタート時間約 50ms約 30ms
3メモリ使用量約 80MB約 60MB
4リクエスト処理時間約 15ms約 8ms

Fastify 版の方がコールドスタート、ウォームスタートともに高速で、メモリ使用量も少ないことがわかります。 Serverless 環境では特にコールドスタートが課題になるため、Fastify の採用は有効な選択肢でしょう。

実行環境別のアーキテクチャ図

最後に、3 つの実行環境(従来型サーバー、コンテナ、Serverless)での NestJS のアーキテクチャを比較してみます。

mermaidflowchart TB
  subgraph traditional ["従来型サーバー (EC2/VPS)"]
    t_lb["ロードバランサー"]
    t_nest1["NestJS<br/>インスタンス 1"]
    t_nest2["NestJS<br/>インスタンス 2"]
    t_db[("データベース")]
    t_lb --> t_nest1
    t_lb --> t_nest2
    t_nest1 --> t_db
    t_nest2 --> t_db
  end

  subgraph container ["コンテナ (ECS/Kubernetes)"]
    c_ingress["Ingress / ALB"]
    c_pod1["Pod 1<br/>(NestJS)"]
    c_pod2["Pod 2<br/>(NestJS)"]
    c_pod3["Pod 3<br/>(NestJS)"]
    c_db[("データベース")]
    c_ingress --> c_pod1
    c_ingress --> c_pod2
    c_ingress --> c_pod3
    c_pod1 --> c_db
    c_pod2 --> c_db
    c_pod3 --> c_db
  end

  subgraph serverless ["Serverless (Lambda)"]
    s_apigw["API Gateway"]
    s_lambda1["Lambda<br/>インスタンス 1"]
    s_lambda2["Lambda<br/>インスタンス 2"]
    s_lambdaN["Lambda<br/>インスタンス N"]
    s_db[("データベース")]
    s_apigw --> s_lambda1
    s_apigw --> s_lambda2
    s_apigw --> s_lambdaN
    s_lambda1 --> s_db
    s_lambda2 --> s_db
    s_lambdaN --> s_db
  end

従来型サーバーとコンテナ環境では、インスタンスが常時起動しているのに対し、Serverless 環境ではリクエストに応じて動的にインスタンスが生成されます。 この違いが、コスト構造やスケーリングの特性に影響しますね。

まとめ

NestJS は 2025 年現在も進化を続けており、Express、Fastify、Serverless と多様な実行環境に対応できる柔軟性が大きな魅力です。

この記事で解説した主なポイントを振り返りましょう。

アダプターの選択基準:

  • Express: エコシステムが豊富で、安定性を重視する場合に最適
  • Fastify: 高速性と型安全性を求める場合や、モダンな開発環境に適している

実行環境の選択:

  • 従来型サーバー: 長時間実行や複雑な処理に向いている
  • コンテナ: スケーラビリティと移植性を両立できる
  • Serverless: 小規模 API やイベント駆動処理でコスト削減が可能

Serverless での注意点:

  • コールドスタート対策として、Fastify や依存パッケージの軽量化を検討する
  • ステートレス設計を意識し、外部ストレージを活用する
  • プロビジョンド同時実行数などの AWS 機能で、レイテンシを改善できる

プロジェクトの要件やチームのスキルセットに応じて、最適な組み合わせを選択することが成功への近道です。 まずは Express でプロトタイプを作成し、パフォーマンスが求められる部分で Fastify を試すといった段階的なアプローチもおすすめですね。

NestJS の柔軟なアーキテクチャを活かして、効率的で保守性の高いアプリケーション開発を実現していきましょう。

関連リンク