Fastifyの詳細:高性能Node.jsウェブフレームワーク
Olivia Novak
Dev Intern · Leapcell

Fastify: 高性能Node.jsウェブフレームワーク
Fastifyは、最適なパフォーマンスを提供するように設計された、効率的で高速なNode.jsウェブフレームワークです。比較的新しいにもかかわらず、その高いパフォーマンスと低いオーバーヘッドにより、多くの開発者に支持されています。Fastifyは、簡潔な開発エクスペリエンスを提供し、高速ルーティングとプラグインアーキテクチャをサポートすることで、開発者がアプリケーションを構築および拡張するのを容易にします。Fastifyの公式ウェブサイトを参照して、以下ではFastifyを多角的に紹介します。
1. 主な機能と原則
- 高パフォーマンス: Fastifyは、最も高速なウェブフレームワークの1つです。コードの複雑さによっては、1秒あたり最大30,000件のリクエストを処理できます。
- スケーラビリティ: フック、プラグイン、デコレータの助けを借りて、Fastifyは完全にスケーラブルです。
- スキーマベース: 必須ではありませんが、ルートを検証し、出力をシリアライズするためにJSONスキーマを使用することをお勧めします。Fastifyは、スキーマを高性能な関数にコンパイルします。
- ロギング: 最高のロガーであるPinoを選択すると、ロギングのコストがほぼなくなります。
- 開発者フレンドリー: このフレームワークは、パフォーマンスとセキュリティを犠牲にすることなく、開発者の日常的な使用にとって非常に表現力豊かで便利です。
- TypeScript対応: 成長を続けるTypeScriptコミュニティをサポートするために、TypeScript型定義ファイルを維持するよう努めています。
2. 誕生の背景
Fastifyが登場する前は、ExpressとKoaがNode.js分野で広く使用されている2つのフレームワークでした。これらは人気があり使いやすいものの、大量のリクエストを処理する際のパフォーマンスは最適ではありません。Fastifyは、次の主要な問題を解決することを目的としています。
- パフォーマンス: 各リクエストのオーバーヘッドを削減することにより、既存のフレームワークよりも高いパフォーマンスを提供し、より多くのリクエストを処理できます。これは、高性能アプリケーションを構築する上で非常に重要です。
- 開発効率: プラグインアーキテクチャとすぐに使える機能(スキーマ検証、ロギングなど)の助けを借りて、効率的で使いやすい開発環境を提供し、開発プロセスを加速し、潜在的なエラーを減らします。
- スキーマ検証: JSONスキーマのサポートが組み込まれており、クライアント入力を検証するだけでなく、アプリケーションのデータ一貫性と信頼性も保証します。これは、多くのフレームワークがコアレベルで統合していない機能です。
- スケーラビリティ: プラグインアーキテクチャにより、開発者はアプリケーション全体のパフォーマンスに大きな影響を与えることなく、新しい機能を追加したり、サードパーティライブラリを統合したりできます。
上記の問題を解決することにより、Fastifyは高性能で保守しやすく、開発しやすいNode.jsアプリケーションを開発するための強力なプラットフォームを提供し、JavaScriptコミュニティで急速に注目を集め、使用されています。
3. 高性能の実現
Fastifyの高いパフォーマンスの鍵は、その設計とアーキテクチャの選択にあり、これにより各リクエストのオーバーヘッドを最小限に抑えることができます。
- 効率的なルーティング分散: Fastifyは、高速ルーティング分散メカニズムを採用しています。リクエストが来たときに、呼び出す処理関数を迅速に決定できるため、リクエスト処理時間が大幅に短縮されます。
- プリコンパイルされたシリアライゼーション: 応答を送信する前に、Fastifyは、実行時にオブジェクトを動的にシリアライズする代わりに、プリコンパイルされたシリアライゼーション関数を使用します。これにより、応答のシリアライズが高速化され、クライアントに送信されます。
- スキーマ駆動開発: Fastifyは、ルートの入出力を定義するためにJSONスキーマを使用することを強く推奨(また、場合によっては必須)しています。これにより、APIを検証および文書化するだけでなく、Fastifyが検証ロジックをプリコンパイルして、ランタイム効率を向上させることができます。
- 最適化されたプラグインシステム: Fastifyのプラグインアーキテクチャは効率的に設計されており、コアフレームワークを軽量に保ちながら、コードの再利用とベストプラクティスをサポートし、パフォーマンスを犠牲にすることなく、高度なモジュール性と柔軟性を実現します。
- 効率的なロギング: 組み込みのロギングツールであるPinoは、速度を重視して設計されており、非常に低いオーバーヘッドでロギング機能を提供します。これは、アプリケーションのパフォーマンスを維持する上で非常に重要です。
- ゼロコスト抽象化: Fastifyの設計哲学は、抽象化レイヤーのパフォーマンスオーバーヘッドを最小限に抑えることです。さまざまな抽象化と便利な機能を使用する場合でも、高いパフォーマンスを維持できます。
これらの設計上の決定と最適化を通じて、Fastifyは優れたパフォーマンスを提供し、効率的で応答性の高いNode.jsアプリケーションを構築するための理想的な選択肢です。
4. インストールと使用法
4.1 npm経由でFastifyをインストールする
npm install fastify
4.2 簡単なコード例
// フレームワークをインポートしてインスタンス化します import Fastify from "fastify"; const fastify = Fastify({ logger: true, }); // ルートを宣言します fastify.get("/", async function handler(request, reply) { return { hello: "world" }; }); // サーバーを実行します! try { await fastify.listen({ port: 3000 }); } catch (err) { fastify.log.error(err); process.exit(1); }
4.3 cliを使用してスケルトンを生成する
npm install --global fastify-cli fastify generate newproject
4.4 JSONスキーマとフックを使用したリクエスト処理の例
import Fastify from "fastify"; const fastify = Fastify({ logger: true, }); fastify.route({ method: "GET", url: "/", schema: { // リクエストには、`name`パラメーターを持つクエリ文字列が必要です querystring: { type: "object", properties: { name: { type: "string" }, }, required: ["name"], }, // レスポンスは、タイプが「string」の`hello`プロパティを持つオブジェクトである必要があります response: { 200: { type: "object", properties: { hello: { type: "string" }, }, }, }, }, // この関数は、ハンドラーの実行前にすべてのリクエストに対して実行されます preHandler: async (request, reply) => { // 例:認証をチェックします }, handler: async (request, reply) => { return { hello: "world" }; }, }); try { await fastify.listen({ port: 3000 }); } catch (err) { fastify.log.error(err); process.exit(1); }
5. プラグイン、デコレータ、ミドルウェア、フック
Fastifyでは、プラグイン、デコレータ、ミドルウェア、およびフックはフレームワークのコアコンセプトであり、それぞれが異なる役割を果たします。
5.1 プラグイン
プラグインは、Fastifyアプリケーションで機能を追加したり、コードを共有したり、ロジックをカプセル化したりするための主な方法です。プラグインは、Fastifyインスタンス、オプション、およびコールバック関数をパラメーターとして受け取る関数にすることができます。プラグインは、ルートの登録、デコレータの追加、新しいフックの宣言、他のプラグインのカプセル化を行うことができ、モジュール式アプリケーションの構築に使用されます。開発者は、これらを使用して再利用可能なロジックブロックを構築し、さまざまなFastifyアプリケーションまたは同じアプリケーションのさまざまな部分で共有できます。
5.2 デコレータ
デコレータは、Fastifyインスタンス、リクエスト(Request)、および応答(Reply)オブジェクトを拡張するために使用されます。新しいメソッドまたはプロパティを追加することにより、開発者はカスタム関数またはデータを追加し、アプリケーションのさまざまな部分で利用できるようにすることができます。たとえば、共有構成データまたはサービスにアクセスするために、各リクエストにメソッドを追加するデコレータを追加できます。
5.3 ミドルウェア
Fastifyはミドルウェアに依存するように設計されていませんが、特定の機能の互換性または統合のために、Express / Connectスタイルのミドルウェアの使用をサポートしています。ミドルウェアは、リクエストオブジェクトとレスポンスオブジェクトにアクセスし、コードを実行し、リクエストオブジェクトとレスポンスオブジェクトを変更し、リクエスト処理チェーンを終了するか、コールスタック内の次のミドルウェアを呼び出すことができます。Fastifyミドルウェアを使用する場合は、不適切な使用がFastifyの一部の最適化をバイパスし、パフォーマンスに影響を与える可能性があるため、注意が必要です。
5.4 フック
フックはFastifyのメカニズムであり、開発者はリクエストライフサイクルのさまざまな段階(リクエストの受信後、ルートの解決前、応答の送信前など)でロジックに介入して実行できます。フックは、許可チェック、リクエストのロギング、応答の変更など、前処理または後処理ロジックを実行するために使用できます。Fastifyは、さまざまなタイプのフック(onRequest、preHandler、onSendなど)を提供し、開発者はリクエスト処理のさまざまな段階を細かく制御できます。
これらのコンポーネントは連携して、フレームワークの高性能特性を維持しながら、Fastifyに優れた柔軟性とスケーラビリティを提供します。
5.5 ライフサイクルウェイト
Fastifyのコンポーネント(プラグイン、デコレータ、ミドルウェア、およびフック)は、特定の実行順序と優先順位に従います。これにより、リクエスト処理フローでのアクションのタイミングが決まります。この実行フローを理解することは、効率的で信頼性の高いFastifyアプリケーションを設計する上で非常に重要です。
- プラグイン: アプリケーションの起動時にロードされ、登録順に実行されます。アプリケーションの起動後、プラグインの設定が固定され、プラグインで定義された関数(ルート、フック、デコレータなど)を後続の各リクエストに使用できます。
- デコレータ: 明確な実行時間はなく、デコレートされたオブジェクト(Fastifyインスタンス、リクエスト、応答)に追加されたメソッドまたはプロパティはすぐに利用可能になり、オブジェクトのライフサイクル全体を通じて有効なままです。
- ミドルウェア: 各リクエストの処理フローの早い段階で、特にルートマッチングの前に実行されます。ミドルウェアは、リクエストオブジェクトと応答オブジェクトを変更したり、リクエストを次のプロセッサに渡すかどうかを決定したりできます。
- フック: 特定の実行順序に従います。これは、リクエスト処理フローに反映されます。
- onRequest: 他の処理の前に、リクエストの受信直後に実行されます。
- preParsing: リクエスト本文が解析される前に実行されます。
- preValidation: ルートレベルの検証の前に実行されます。
- preHandler: ルート処理関数の前に実行されます。
- preSerialization: レスポンスがシリアル化される前、クライアントに送信される前に実行されます。
- onSend: レスポンスがクライアントに送信される前、ただしシリアル化後に実行されます。
- onResponse: レスポンスがクライアントに完全に送信された後に実行されます。
中断する機能
- プラグイン: リクエスト処理の中断に直接参加しませんが、プロセスに影響を与えるフックまたはミドルウェアを登録できます。
- デコレータ: プロセスを制御しないため、中断には参加しません。
- ミドルウェア: リクエスト処理フローを中断できます。たとえば、next()を呼び出さないか、応答を送信すると、後続の処理が停止します。
- フック: 特定のフック(preHandlerなど)は、リクエストの処理を続行するか、応答を直接送信するかを決定できるため、後続のプロセスが中断されます。
これらのコンポーネントの実行順序と割り込み機能を理解することは、期待どおりに動作するFastifyアプリケーションを構築する上で非常に重要です。
6. ライフサイクル
通常のプロセス
- [受信リクエスト]: 新しいリクエストがシステムに入ります。
- ↓
- [ルーティング]: リクエストに対応するルートを決定します。
- ↓
- [インスタンスロガー]: リクエストに関連するインスタンスログを記録します。
- ↓
- [onRequestフック]:
onRequest
フック関数を実行します。これは、統合されたリクエストの前処理に使用できます。 - ↓
- [preParsingフック]: リクエスト本文が解析される前にフック関数を実行します。
- ↓
- [解析]: リクエスト本文を解析します。
- ↓
- [preValidationフック]: ルート検証の前にフック関数を実行します。
- ↓
- {検証}: ルート検証を実行します
- 検証に失敗した場合→[400]: 400エラー応答を返します。
- 検証に合格した場合→↓
- [preHandlerフック]: ルート処理関数の前にフック関数を実行すると、許可チェックなどの操作を実行できます。
- ↓
- [ユーザーハンドラー]: ユーザー定義のルート処理関数を実行します。
- ↓
- [応答]: 応答コンテンツを準備します。
- ↓
- [preSerializationフック]: 応答がシリアル化される前にフック関数を実行します。
- ↓
- [onSendフック]: 応答が送信される前にフック関数を実行します。
- ↓
- [送信応答]: 応答を送信します。
- ↓
- [onResponseフック]: 応答が完全に送信された後にフック関数を実行します。
7. カプセル化
Fastifyのカプセル化コンテキスト
は、その基本的な機能の1つです。カプセル化コンテキストは、ルートに使用できるデコレータ、登録されたフック、およびプラグインを決定します。各コンテキストは、親コンテキストからのみ継承し、親コンテキストは、その子孫コンテキスト内のエンティティにアクセスできません。 デフォルトの状態が要件を満たしていない場合は、fastify-pluginを使用してカプセル化コンテキストを中断し、子孫コンテキストに登録されたコンテンツを、含まれている親コンテキストで使用できるようにすることができます。
8. ルートのJSONスキーマ検証
JSONスキーマは、Fastifyのルートと組み合わせて、クライアントのリクエストデータを検証し、レスポンスデータをフォーマットします。JSONスキーマを定義することにより、受信リクエストデータが特定の形式とタイプの要件を満たしていることを確認すると同時に、レスポンスのデータ構造を制御し、APIの堅牢性とセキュリティを向上させることができます。
コード例
const fastify = require("fastify")(); // JSONスキーマを定義します const userSchema = { type: "object", required: ["username", "email", "gender"], properties: { username: { type: "string" }, email: { type: "string", format: "email" }, gender: { type: "string", minimum: "male" }, }, }; fastify.route({ method: "POST", url: "/create-user", schema: { body: userSchema, // JSONスキーマを使用してリクエスト本文を検証します response: { 200: { type: "object", properties: { success: { type: "boolean" }, id: { type: "string" }, }, }, }, }, handler: (request, reply) => { // リクエストロジックを処理します // ユーザーの作成に成功し、ユーザーIDを返すことを想定します reply.send({ success: true, id: "leapcell" }); }, }); fastify.listen(3000, (err) => { if (err) throw err; console.log("Server is running on http://localhost:3000"); });
この例では:
userSchema
が定義され、必要なフィールドや各フィールドのタイプなど、リクエスト本文の予期される形式を記述します。- ルート構成では、
userSchema
がschema
プロパティを介してリクエスト本文(body
)に適用され、Fastifyは、受信リクエストデータが定義されたスキーマに準拠しているかどうかを自動的に検証します。 - レスポンスの
schema
が定義され、レスポンスデータ構造が期待どおりになるようにします。
このようにして、すべての受信リクエストが厳密に検証され、すべてのレスポンスが事前定義された形式に準拠していることを確認でき、APIの堅牢性とユーザーの予測可能性が向上します。
9. 例
9.1 フックによるログインステータスの問題の処理
const fastify = require("fastify")({ logger: true }); const secureSession = require("fastify-secure-session"); // セッションプラグインを登録します fastify.register(secureSession, { secret: "averylognsecretkey", // 長くて複雑な秘密鍵を使用する必要があります cookie: { path: "/", // 必要に応じて、他のCookieオプションを構成します }, }); // preHandlerフックを/api/aと/api/cのみに適用します fastify.addHook("preHandler", (request, reply, done) => { if ( (request.routerPath === "/api/1" || request.routerPath === "/api/2") && !request.session.user ) { request.session.redirectTo = request.raw.url; reply.redirect(302, "/login"); done(); } else { done(); } }); // セッション検証を必要としないルート fastify.get("/api/2", (req, reply) => { reply.send({ message: "/api/2へのアクセスが許可されました" }); }); // セッション検証を必要とするルート fastify.get("/api/1", (req, reply) => { reply.send({ message: "/api/1へのアクセスが許可されました" }); }); fastify.get("/api/3", (req, reply) => { reply.send({ message: "/api/3へのアクセスが許可されました" }); }); // ログインページのルート fastify.get("/login", (req, reply) => { reply.send(`ログインフォームがここにあります。認証するには/ loginにPOSTしてください。`); }); // ログインロジック fastify.post("/login", (req, reply) => { const { username, password } = req.body; if (username === "user" && password === "password") { req.session.user = username; const redirectTo = req.session.redirectTo || "/"; delete req.session.redirectTo; reply.redirect(302, redirectTo); } else { reply.code(401).send({ error: "Unauthorized" }); } }); // サーバーを起動します fastify.listen(3000, (err) => { if (err) { fastify.log.error(err); process.exit(1); console.log(`Server listening on http://localhost:3000`); });
9.2 パラメータの制限
次の例では、a
パラメーターとb
パラメーターがPOSTリクエストで渡される必要があります。
const fastify = require("fastify")(); const postSchema = { schema: { body: { type: "object", required: ["a", "b"], properties: { a: { type: "string" }, b: { type: "string" } } }, response: { // 見つからない場合は、デフォルトで400エラーが返され、400の戻り値をここで変更できます 400: { type: "object", properties: { statusCode: { type: "number" }, error: { type: "string" }, message: { type: "string" } } } }, // ここでは、上記のデフォルトのインターセプト戦略をインターセプトできます preValidation: (request, reply, done) => { if (!request.body ||!request.body.a ||!request.body.b) { reply .code(400) .send({ error: "Bad Request", message: "Missing required parameters" }); return; } done(); } }; fastify.post("/api/data", postSchema, (request, reply) => { // ここに到達した場合、aパラメーターとbパラメーターの両方が検証に合格したことを意味します reply.send({ message: "Success" }); }); fastify.listen(3000, (err) => { if (err) { console.error(err); process.exit(1); } console.log("Server listening on port 3000"); });
ルートでpreValidation
フックとカスタムresponse schema
を定義しない場合、POSTリクエストのbody
が定義されたJSONスキーマに準拠していない場合、Fastifyはデフォルトで400エラー(Bad Request)を返します。このエラーのレスポンス本文には、要件を満たしていないフィールドに関する情報が含まれますが、この情報はFastifyによって自動的に生成され、カスタムエラーメッセージほど具体的または明確ではない場合があります。 デフォルトでは、a
パラメーターのみが渡され、b
パラメーターがない場合、Fastifyは次のようなエラー詳細を含むレスポンスを返します。
{ "statusCode": 400, "error": "Bad Request", "message": "body should have required property 'b'" }
Leapcell:最高のサーバーレスWebホスティング
最後に、NodeJSサービスのデプロイに最適なプラットフォームをお勧めします。Leapcell
🚀 お気に入りの言語で構築する
JavaScript、Python、Go、Rustで簡単に開発できます。
🌍 無制限のプロジェクトを無料でデプロイする
使った分だけ支払う—リクエストも料金もありません。
⚡ 従量課金制、隠れたコストなし
アイドル料金は発生せず、シームレスなスケーラビリティだけです。
🔹 Twitterでフォローしてください:@LeapcellHQ