はじめに
Node.jsでAPIサーバーを作るとき、多くの人がまずExpressを選ぶと思います。自分もそうでした。でも、ある程度の規模になってくると「もっと速くならないかな」と思う瞬間が来るんですよね。
そこで出会ったのがFastifyです。
Fastifyは、Node.js向けの高性能Webフレームワークで、公式によると秒間76,000リクエスト以上を処理できるとのこと。GitHub Starsは35,000を超えていて、Mercedes-BenzやLambdaTestといった大手企業でも採用されています。月間ダウンロード数は1,300万件以上。
正直なところ、最初は「また新しいフレームワークか」と思っていたんですが、実際に使ってみると納得の性能でした。今回はそのFastifyについて、実務で使えるレベルまで解説していきます。
特徴・メリット
1. 圧倒的なパフォーマンス
これ、意外と重要なポイントなんですが、Fastifyはフレームワークのオーバーヘッドを極限まで削減しています。ベンチマークを見ると、Expressと比較して2〜3倍速いケースもあるようです。
内部的にはJSONのシリアライズを最適化していて、fast-json-stringifyというライブラリを使っています。スキーマを事前に定義しておくことで、JSON.stringifyより高速に処理できる仕組みですね。
2. プラグインアーキテクチャ
個人的にはこれが一番気に入っているポイントです。Fastifyはプラグインベースの設計になっていて、機能を疎結合に保てます。
公式エコシステムには308個以上のプラグインがあって、認証、データベース接続、CORS対応など、必要なものはだいたい揃っています。自分でプラグインを作るのも簡単なので、チーム開発でも使いやすいですね。
3. JSON Schemaによるバリデーション
リクエストとレスポンスのバリデーションをJSON Schemaで定義できます。これがなかなか便利で、APIのドキュメント生成にも活用できるんですよ。
型安全性を担保しながら、パフォーマンスも落とさない。このバランスは30代のエンジニアとしては嬉しいところです。時短にもなりますしね。
4. TypeScriptとの相性
最近のプロジェクトでTypeScriptを使わないことはほぼないと思いますが、FastifyはTypeScriptを公式サポートしています。型定義がしっかりしていて、開発体験がかなり良いです。
5. Pinoロガーの統合
ロギングには高速なPinoが組み込まれています。本番環境でのログ出力コストを最小化できるので、パフォーマンスを重視するプロジェクトには嬉しい機能ですね。
インストール方法
インストールは非常にシンプルです。
# npmの場合
npm install fastify
# yarnの場合
yarn add fastify
# pnpmの場合
pnpm add fastify
TypeScriptを使う場合は、型定義が同梱されているので追加インストールは不要です。これ地味にありがたいですよね。
基本的な使い方
最小構成のサーバー
まずは最小構成から見ていきましょう。
// ESM形式
import Fastify from 'fastify'
const fastify = Fastify({
logger: true
})
// ルート定義
fastify.get('/', async (request, reply) => {
return { hello: 'world' }
})
// サーバー起動
const start = async () => {
try {
await fastify.listen({ port: 3000 })
} catch (err) {
fastify.log.error(err)
process.exit(1)
}
}
start()
Expressを使ったことがある人なら、違和感なく読めると思います。async/awaitがネイティブでサポートされているのもポイントですね。
TypeScriptでの実装
TypeScriptで書くとこんな感じになります。
import Fastify, { FastifyRequest, FastifyReply } from 'fastify'
const fastify = Fastify({
logger: true
})
interface QueryParams {
name?: string
}
fastify.get<{ Querystring: QueryParams }>(
'/hello',
async (request: FastifyRequest<{ Querystring: QueryParams }>, reply: FastifyReply) => {
const { name } = request.query
return { message: `Hello, ${name || 'World'}!` }
}
)
fastify.listen({ port: 3000 })
ジェネリクスを使ってリクエストの型を指定できるので、型安全な開発ができます。
JSON Schemaによるバリデーション
リクエストのバリデーションを追加してみましょう。
import Fastify from 'fastify'
const fastify = Fastify({ logger: true })
const userSchema = {
body: {
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string', minLength: 1 },
email: { type: 'string', format: 'email' },
age: { type: 'integer', minimum: 0 }
}
},
response: {
200: {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' },
email: { type: 'string' }
}
}
}
}
fastify.post('/users', { schema: userSchema }, async (request, reply) => {
const { name, email, age } = request.body as any
// ここでユーザー作成処理
return { id: 1, name, email }
})
fastify.listen({ port: 3000 })
スキーマを定義しておくと、バリデーションエラー時に自動でエラーレスポンスを返してくれます。コスパ的にかなり良いですね。
プラグインの作成と利用
Fastifyの強みであるプラグイン機能も見ておきましょう。
import Fastify, { FastifyPluginAsync } from 'fastify'
import fp from 'fastify-plugin'
// データベース接続プラグイン
const dbPlugin: FastifyPluginAsync = async (fastify) => {
// 実際はここでDB接続処理
const db = { query: async (sql: string) => ({ rows: [] }) }
fastify.decorate('db', db)
}
// プラグインとして登録
const wrappedDbPlugin = fp(dbPlugin)
const fastify = Fastify({ logger: true })
// プラグイン登録
fastify.register(wrappedDbPlugin)
// 登録後はfastify.dbでアクセス可能
fastify.get('/users', async (request, reply) => {
const result = await fastify.db.query('SELECT * FROM users')
return result.rows
})
fastify.listen({ port: 3000 })
fastify-pluginを使うと、プラグインのスコープを親インスタンスに公開できます。チーム開発では共通処理をプラグイン化しておくと、コードの見通しが良くなりますよ。
実践的なユースケース
REST APIサーバー
実務で一番多いのはREST APIサーバーとしての利用でしょう。
import Fastify from 'fastify'
import cors from '@fastify/cors'
import helmet from '@fastify/helmet'
const fastify = Fastify({ logger: true })
// セキュリティ系プラグイン
await fastify.register(cors, { origin: true })
await fastify.register(helmet)
// ルートのプレフィックス付きで登録
fastify.register(
async (app) => {
app.get('/', async () => ({ items: [] }))
app.get('/:id', async (request) => {
const { id } = request.params as { id: string }
return { id, name: 'Item' }
})
app.post('/', async (request) => {
return { id: Date.now(), ...request.body as object }
})
},
{ prefix: '/api/items' }
)
fastify.listen({ port: 3000 })
プレフィックスを使ったルーティングのグループ化は、APIのバージョニングにも使えて便利です。
マイクロサービスのエンドポイント
Fastifyの軽量さは、マイクロサービスアーキテクチャとの相性が良いです。
import Fastify from 'fastify'
const fastify = Fastify({
logger: true,
requestTimeout: 5000, // タイムアウト設定
bodyLimit: 1048576 // 1MB制限
})
// ヘルスチェックエンドポイント
fastify.get('/health', async () => ({ status: 'ok', timestamp: Date.now() }))
// メトリクス用エンドポイント
fastify.get('/metrics', async () => {
return {
uptime: process.uptime(),
memory: process.memoryUsage(),
cpu: process.cpuUsage()
}
})
fastify.listen({ port: 3000, host: '0.0.0.0' })
Kubernetesなどのコンテナ環境でも、起動が速いので重宝します。
WebSocketとの併用
リアルタイム通信が必要な場合は、@fastify/websocketプラグインを使います。
import Fastify from 'fastify'
import websocket from '@fastify/websocket'
const fastify = Fastify({ logger: true })
await fastify.register(websocket)
fastify.get('/ws', { websocket: true }, (socket, request) => {
socket.on('message', (message) => {
socket.send(`Echo: ${message}`)
})
})
fastify.listen({ port: 3000 })
HTTPとWebSocketを同じサーバーで提供できるので、チャットアプリやリアルタイムダッシュボードの構築に向いています。
ExpressからFastifyへの移行
既存のExpressプロジェクトから移行したい場合は、@fastify/expressプラグインで段階的に移行できます。
import Fastify from 'fastify'
import expressPlugin from '@fastify/express'
import expressMiddleware from 'some-express-middleware'
const fastify = Fastify()
await fastify.register(expressPlugin)
// 既存のExpressミドルウェアを利用
fastify.use(expressMiddleware())
// 新規ルートはFastify形式で
fastify.get('/new-route', async () => ({ fast: true }))
fastify.listen({ port: 3000 })
一気に書き換えるのではなく、少しずつ移行できるのは現実的な選択肢だと思います。
まとめ
Fastifyは、パフォーマンスと開発体験のバランスが取れた良いフレームワークだと感じています。
個人的なおすすめポイント
- 新規プロジェクトでNode.jsのAPIサーバーを作るなら、Fastify一択ですね
- TypeScriptとの相性が良く、型安全な開発ができる
- プラグインエコシステムが充実していて、車輪の再発明を避けられる
- パフォーマンスチューニングの手間が減って、QOL上がります
30代になって思うのは、「速いは正義」ということ。ユーザー体験の向上にも、サーバーコストの削減にも、パフォーマンスは直結します。Expressに不満を感じている方は、一度Fastifyを試してみる価値はあると思います。
公式ドキュメントも充実していて、日本語の情報も増えてきました。学習コストはそこまで高くないので、週末にでも触ってみてはいかがでしょうか。