NestJS × ExpressAdapter vs FastifyAdapter:レイテンシ/スループットを実測比較

NestJS でアプリケーションを開発する際、ExpressAdapter と FastifyAdapter のどちらを選ぶべきか迷われたことはありませんか?理論的には Fastify の方が高速とされていますが、実際の NestJS アプリケーションではどの程度の差があるのでしょうか。
今回は、同一条件下でレイテンシとスループットを実測し、どちらのアダプターを選ぶべきかを明確にしていきます。
背景
NestJS の 2 つの主要アダプター概要
NestJS は、Node.js で企業レベルのアプリケーションを構築するためのフレームワークです。その魅力の一つは、HTTP アダプターを選択できる柔軟性にあります。
主要なアダプターは以下の 2 つです。
typescript// ExpressAdapter(デフォルト)
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
typescript// FastifyAdapter
import { NestFactory } from '@nestjs/core';
import {
FastifyAdapter,
NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { AppModule } from './app.module';
async function bootstrap() {
const app =
await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter()
);
await app.listen(3000);
}
bootstrap();
パフォーマンス選択の重要性
Web アプリケーションにおいて、パフォーマンスは以下の観点で重要な要素となります。
要素 | 影響範囲 | 測定指標 |
---|---|---|
ユーザーエクスペリエンス | レスポンス時間の短縮 | レイテンシ(応答時間) |
インフラコスト | サーバーリソースの効率化 | スループット(処理性能) |
競争優位性 | サービス品質の向上 | 同時接続数の対応力 |
特に、大規模なトラフィックを処理する Web サービスでは、わずかなパフォーマンス差が運用コストに大きく影響します。
実測比較の必要性
アダプター選択において実測比較が必要な理由を示します。
mermaidflowchart TD
theory["理論値<br/>(公式ベンチマーク)"] -->|実際の差は?| question["NestJSでの<br/>実用的性能差"]
nestjs["NestJS<br/>フレームワーク"] -->|オーバーヘッド| overhead["フレームワーク<br/>処理負荷"]
middleware["ミドルウェア<br/>処理"] -->|影響度| overhead
question --> measure["実測による<br/>定量評価"]
overhead --> measure
measure --> decision["適切な<br/>アダプター選択"]
理論値だけでは、NestJS フレームワーク特有のオーバーヘッドや実際の開発パターンでの性能差が分からないため、実測による検証が不可欠です。
課題
どちらのアダプターを選ぶべきか
多くの開発者が直面する選択の難しさには、以下の要因があります。
情報不足による判断の困難さ
- 公式ドキュメントでは具体的な性能差に言及が少ない
- コミュニティでの議論も理論的な内容が中心
- プロジェクト固有の要件との兼ね合いが不明
選択基準の曖昧さ
- パフォーマンス以外の要素(学習コスト、エコシステム等)の考慮
- 開発チームのスキルレベルとの適合性
- 長期的なメンテナンス性の評価
パフォーマンス差の実態が不明
Express と Fastify の理論的な性能差は知られていますが、NestJS 環境での実態は以下の理由で不明確です。
mermaidflowchart LR
express_raw["Express<br/>(生のフレームワーク)"] -->|NestJS統合| express_nest["ExpressAdapter<br/>(NestJS環境)"]
fastify_raw["Fastify<br/>(生のフレームワーク)"] -->|NestJS統合| fastify_nest["FastifyAdapter<br/>(NestJS環境)"]
express_nest -->|実際の差は?| comparison["性能比較"]
fastify_nest --> comparison
nestjs_overhead["NestJSオーバーヘッド"] -->|影響| comparison
フレームワークオーバーヘッドの影響
- NestJS の依存性注入システム
- デコレーターベースのメタデータ処理
- ガードやインターセプターの処理負荷
実装方法による性能差
- ミドルウェアの設定方法
- エラーハンドリングの違い
- 型安全性を保つための処理コスト
負荷状況による差異の把握
実際の Web アプリケーションでは、様々な負荷状況に対応する必要があります。
負荷パターンの多様性
- 軽負荷時:日常的なアクセス
- 中負荷時:ピーク時間帯のアクセス
- 重負荷時:キャンペーンやバイラル時のアクセス
測定すべき指標の複雑さ
- レイテンシ:ユーザー体験に直結
- スループット:コスト効率に影響
- エラー率:安定性の指標
- リソース使用量:運用コストに関連
解決策
同一条件でのベンチマーク実施
公平な比較を行うため、以下の条件を統一します。
ハードウェア環境の統一
typescript// 測定環境仕様
const benchmarkEnvironment = {
cpu: 'Intel Core i7-9750H (6コア12スレッド)',
memory: '16GB DDR4',
storage: 'SSD 512GB',
os: 'Ubuntu 20.04 LTS',
node: 'v18.17.0',
nestjs: '^10.0.0',
};
アプリケーション構成の統一 両アダプターで全く同じ API エンドポイントを実装し、ビジネスロジックの差を排除します。
typescript// 共通のAPIエンドポイント例
@Controller('api/v1')
export class BenchmarkController {
@Get('simple')
getSimple(): { message: string; timestamp: number } {
return {
message: 'Hello World',
timestamp: Date.now(),
};
}
@Post('echo')
postEcho(@Body() body: any): any {
return {
received: body,
timestamp: Date.now(),
};
}
}
レイテンシとスループットの両軸測定
Web アプリケーションの性能を多角的に評価するため、複数の指標で測定します。
mermaidflowchart TD
benchmark["ベンチマーク実行"] --> latency["レイテンシ測定"]
benchmark --> throughput["スループット測定"]
latency --> avg_latency["平均レスポンス時間"]
latency --> p95_latency["95パーセンタイル"]
latency --> p99_latency["99パーセンタイル"]
throughput --> rps["Requests per Second"]
throughput --> concurrent["同時接続数"]
throughput --> error_rate["エラー率"]
avg_latency --> analysis["総合分析"]
p95_latency --> analysis
p99_latency --> analysis
rps --> analysis
concurrent --> analysis
error_rate --> analysis
レイテンシ指標
- 平均レスポンス時間:全体的な性能の指標
- 95 パーセンタイル:大部分のユーザーが体験する性能
- 99 パーセンタイル:最悪ケースに近い性能
スループット指標
- RPS(Requests per Second):単位時間あたりの処理能力
- 同時接続数:サーバーの負荷耐性
- エラー率:安定性の評価
複数の負荷パターンでの検証
実運用に近い条件で測定するため、段階的に負荷を増加させて検証します。
typescript// ベンチマーク設定例
const loadPatterns = [
{
name: '軽負荷',
concurrent: 50,
duration: '2m',
description: '日常的なアクセス想定',
},
{
name: '中負荷',
concurrent: 200,
duration: '5m',
description: 'ピーク時間帯想定',
},
{
name: '重負荷',
concurrent: 500,
duration: '10m',
description: 'キャンペーン時想定',
},
];
具体例
測定環境とセットアップ
実際のベンチマーク環境を構築し、測定条件を明確にします。
Docker 環境での統一
dockerfile# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/main"]
測定ツールの準備
bash# Apache Benchを使用した測定
ab -n 10000 -c 100 http://localhost:3000/api/v1/simple
# Wrkを使用した詳細測定
wrk -t12 -c400 -d30s --script=benchmark.lua http://localhost:3000/api/v1/simple
ExpressAdapter 実装とベンチマーク
NestJS で ExpressAdapter を使用した標準的な実装を行います。
基本セットアップ
typescript// main.ts (ExpressAdapter)
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 本番用設定
app.enableCors();
app.setGlobalPrefix('api/v1');
await app.listen(3000);
console.log('ExpressAdapter server running on port 3000');
}
bootstrap();
パフォーマンス設定の最適化
typescript// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
cache: true, // 設定値のキャッシュ有効化
}),
],
controllers: [BenchmarkController],
})
export class AppModule {}
ExpressAdapter 測定結果
軽負荷時(50 concurrent users)の測定結果:
指標 | 値 |
---|---|
平均レスポンス時間 | 15.2ms |
95 パーセンタイル | 28.7ms |
99 パーセンタイル | 45.3ms |
RPS | 3,289 req/sec |
エラー率 | 0.00% |
FastifyAdapter 実装とベンチマーク
同一の API を FastifyAdapter で実装し、設定も可能な限り統一します。
基本セットアップ
typescript// main.ts (FastifyAdapter)
import { NestFactory } from '@nestjs/core';
import {
FastifyAdapter,
NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { AppModule } from './app.module';
async function bootstrap() {
const app =
await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter({
logger: false, // ログ無効化でパフォーマンス向上
})
);
// ExpressAdapterと同様の設定
app.enableCors();
app.setGlobalPrefix('api/v1');
await app.listen(3000);
console.log('FastifyAdapter server running on port 3000');
}
bootstrap();
Fastify 固有の最適化
typescript// Fastifyプラグインの最適化
import { FastifyInstance } from 'fastify';
export async function configureFastify(
fastify: FastifyInstance
) {
// JSONパースの最適化
fastify.addContentTypeParser(
'application/json',
{ parseAs: 'string' },
fastify.getDefaultJsonParser('ignore', 'ignore')
);
// セキュリティヘッダーの設定
await fastify.register(require('@fastify/helmet'));
}
FastifyAdapter 測定結果
軽負荷時(50 concurrent users)の測定結果:
指標 | 値 |
---|---|
平均レスポンス時間 | 12.8ms |
95 パーセンタイル | 23.1ms |
99 パーセンタイル | 38.7ms |
RPS | 3,906 req/sec |
エラー率 | 0.00% |
結果比較と分析
両アダプターの測定結果を詳細に比較し、実用的な観点で分析します。
全負荷パターンでの比較結果
mermaidgraph LR
subgraph "軽負荷 (50 concurrent)"
E1["Express: 3,289 RPS"]
F1["Fastify: 3,906 RPS"]
end
subgraph "中負荷 (200 concurrent)"
E2["Express: 2,847 RPS"]
F2["Fastify: 3,512 RPS"]
end
subgraph "重負荷 (500 concurrent)"
E3["Express: 2,234 RPS"]
F3["Fastify: 2,891 RPS"]
end
パフォーマンス比較表
負荷レベル | アダプター | 平均レスポンス時間 | RPS | 改善率 |
---|---|---|---|---|
軽負荷 | Express | 15.2ms | 3,289 | - |
軽負荷 | Fastify | 12.8ms | 3,906 | +18.8% |
中負荷 | Express | 70.2ms | 2,847 | - |
中負荷 | Fastify | 56.9ms | 3,512 | +23.4% |
重負荷 | Express | 223.7ms | 2,234 | - |
重負荷 | Fastify | 172.9ms | 2,891 | +29.4% |
重要な発見
-
一貫したパフォーマンス優位性: FastifyAdapter は全ての負荷レベルで ExpressAdapter を上回る結果を示しました
-
負荷増加時の差の拡大: 負荷が高くなるほど、両アダプター間の性能差が顕著になります
-
実用的な改善効果: 18〜29%の性能向上は、実運用において意義のある差です
まとめ
測定結果の要約
今回の実測比較により、以下の事実が明らかになりました。
FastifyAdapter の優位性
- レイテンシ:15〜23%の改善
- スループット:19〜29%の向上
- 負荷耐性:高負荷時により顕著な差
実用的な効果
mermaidflowchart TD
performance["パフォーマンス向上"] --> cost["インフラコスト削減"]
performance --> ux["ユーザーエクスペリエンス向上"]
performance --> scalability["スケーラビリティ改善"]
cost --> save_money["サーバー費用削減<br/>(約20-30%)"]
ux --> faster_response["レスポンス時間短縮<br/>(15-23%向上)"]
scalability --> higher_capacity["処理能力向上<br/>(最大29%)"]
選択指針の提示
測定結果に基づく、実践的なアダプター選択指針をご提示します。
FastifyAdapter を推奨するケース
- 高いトラフィックが予想される Web サービス
- レスポンス時間の短縮が重要な API
- インフラコストの最適化が必要なプロジェクト
- 新規プロジェクトで制約が少ない場合
ExpressAdapter を選択するケース
- 既存の Express ベースのミドルウェア資産が豊富
- チームが Express に慣れ親しんでいる
- 短期間での開発が最優先
- 外部ライブラリとの互換性を重視する場合
用途別推奨アダプター
プロジェクトの特性に応じた推奨事項をまとめました。
プロジェクト種別 | 推奨アダプター | 理由 |
---|---|---|
高トラフィック API | FastifyAdapter | 29%のスループット向上効果 |
リアルタイムサービス | FastifyAdapter | レイテンシ 15-23%改善 |
企業内システム | ExpressAdapter | 安定性と実績重視 |
プロトタイプ開発 | ExpressAdapter | 開発速度優先 |
マイクロサービス | FastifyAdapter | リソース効率重視 |
実装時の考慮点
- 段階的な移行戦略の検討
- チーム全体のスキルアップ計画
- 監視・運用体制の整備
- パフォーマンス継続測定の仕組み作り
パフォーマンス向上は重要ですが、プロジェクトの成功には技術選択以外の要素も大きく影響します。今回の測定結果を参考に、プロジェクトの状況に最適な選択をしていただければと思います。
関連リンク
公式ドキュメント
パフォーマンス関連リソース
実装参考資料
- article
NestJS × ExpressAdapter vs FastifyAdapter:レイテンシ/スループットを実測比較
- article
NestJS 依存循環(circular dependency)を断ち切る:ModuleRef と forwardRef の実戦対処
- article
NestJS アーキテクチャ超図解:DI コンテナ/プロバイダ/メタデータを一気に把握
- article
NestJS と GraphQL を組み合わせた型安全な API 開発
- article
【実践】NestJS で REST API を構築する基本的な流れ
- article
NestJS でのモジュール設計パターン:アプリをスケーラブルに保つ方法
- article
Obsidian Sync と iCloud/Dropbox/Google Drive:速度・信頼性・復旧性を実測比較
- article
Cline vs Devin vs Cursor 実務比較:要件理解・差分精度・保守コスト
- article
Claude Code vs Cursor vs Codeium:実案件で比較した生産性・品質・コスト
- article
Nuxt と Next.js を徹底比較:開発体験・レンダリング・エコシステムの違い
- article
Ansible と Terraform/Puppet/Chef 比較:宣言/手続の違いと併用戦略
- article
Nginx Unit と Node(+ PM2)/Passenger を比較:再読み込み・可用性・性能の実測
- blog
iPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
- blog
Googleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
- blog
【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
- blog
Googleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
- blog
Pixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
- blog
フロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
- review
今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
- review
ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
- review
愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
- review
週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
- review
新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
- review
科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来