Skip to main content

バックエンドNode.js APIを構築するための基盤となるFastifyプラグイン

著者
blog feature snyk container custom base recommendations

2024年5月28日

0 分で読めます

バックエンドNode.js APIの構築において、Fastifyはプラグインエコシステムとアーキテクチャ設計が際立ち、従来のExpressフレームワークに代わる魅力的な選択肢となっています。効率性が高くオーバーヘッドの少ないこのWebフレームワークは、優れた速度とシンプルさを兼ね備えています。Fastifyの設計の中核を成す堅牢なプラグインアーキテクチャは、Node.jsの非同期処理能力を最大限に活用し、Fastifyプラグインの重要な役割を重視することで、Node.jsフレームワークのパフォーマンスに新たな基準を打ち立てています。

Fastifyは成長を続けるオープンソースコミュニティによって開発されており、Webアプリケーション開発の最新トレンドに対応し続けています。2024年にバックエンドWeb開発に取り組むなら、高速でスケーラブルかつ安全なWebアプリケーションを構築するうえで、Fastifyは頼れるフレームワークです。

Fastifyとそのプラグインが堅牢なバックエンドNode.js APIを実現する理由

最新のWebアプリケーション開発において、Fastifyは開発者にとって魅力的な選択肢となっています。Node.js向けに構築されたこのWebフレームワークは、パフォーマンス、柔軟性、開発者に優しい機能を兼ね備えています。このセクションでは、最新のアプリケーション開発にFastifyを使うメリットを解説します。

最新のESMをサポート

FastifyはECMAScriptモジュール(ESM)を直接サポートしています。ESMはJavaScriptパッケージの公式モジュール形式であるため、これは重要です。Fastifyでは、トランスパイルの手順を挟まずにimportとexportの構文をそのまま使えます。

ESMを使ったFastifyサーバーの簡単な例を紹介します。

import fastify from 'fastify';

const server = fastify();

server.get('/', async (request, reply) => {
  return { hello: 'world' }
});

server.listen({port: 3000}, (err, address) => {
  if (err) throw err
  server.log.info(`Server listening at ${address}`)
});

async/awaitとPromiseを使った適切なルート定義

Fastifyでは、async/await構文を使って簡単にルートを処理できます。その結果、従来のコールバックパターンに比べて、コードがすっきりして読みやすくなります。

Expressはルート定義でasync/awaitをネイティブにサポートしていないため、コールバック地獄に陥ったり、エラー処理のためにルートハンドラーをtry/catchブロックで囲む必要が生じたりすることがよくあります。Fastifyはasync/awaitをネイティブにサポートしているため、Promiseを使った非同期コードをより簡単に記述し、保守できます。

Fastifyでルート定義にasync/awaitを使う例を紹介します。

server.get('/api/tasks', async (request, reply) => {
  const tasks = await Tasks.findAll();
  return tasks;
});

高速なラディックス木によるルート解決で高いパフォーマンスを実現

Fastifyはパフォーマンスを重視して設計されています。非常に効率的なルーティングアルゴリズムである、高速なラディックス木によるルート解決を採用しています。これにより、最小限のオーバーヘッドで多数のルートを処理でき、負荷が高い状況でも高速な応答時間を実現します。

カプセル化に対応した豊富なFastifyプラグインアーキテクチャ

Fastifyには、カプセル化に対応した豊富なプラグインアーキテクチャがあります。アプリケーションを、それぞれ独自のルート、プラグイン、デコレーターを持つ独立したコンポーネントに簡単に分割できます。これにより、コードを整理しやすくなり、名前の衝突も起こりにくくなります。

ユーティリティ関数でルートハンドラーを拡張するFastifyプラグインの例を紹介します。

import fp from 'fastify-plugin';

export default fp(async function (fastify, opts) {
  fastify.decorate('utility', () => 'This is a utility function');
});

このプラグインは次のように使います。

// import and get a fastify server instance,
// and then:
server.register(import('./utility-plugin.js'));

server.get('/', async (request, reply) => {
  return server.utility();
});

軽量な依存性注入

Fastifyのプラグインシステムは、軽量な依存性注入(DI)システムとしても機能します。シングルトンやグローバル変数を使わずに、アプリケーション全体で共通のユーティリティやサービスを簡単に共有できます。

次の例では、アプリケーションのさまざまな部分でデータベース接続を共有します。

fastify.register(async function (fastify, opts) {
  const db = await someDbConnectionLibrary(opts.connectionString);

  fastify.decorate('db', db);
});

Fastifyで作るシンプルなバックエンドNode.js API

このブログでは、2024年にFastifyと推奨プラグインを使ってバックエンドNode.js APIを構築するための基本要素を解説します。ルーティングやミドルウェアから、バリデーション、シリアライズまで、Fastifyアプリケーションに欠かせない要素を詳しく見ていきます。

さらに、今日のデジタル社会においてサイバーセキュリティが重要であることを踏まえ、Webセキュリティに関する要素と、それらに対処するためのFastifyプラグインの使い方も簡単に紹介します。これは、アプリケーション開発を急ぐあまり見落とされがちな重要なポイントです。

まず、最新のNode.js LTSバージョンがシステムにインストールされていることを確認してください(執筆時点ではv20.11.1)。次に、新しいNode.jsプロジェクトを初期化し、以下のようにnpmでFastifyをインストールします。

$ mkdir fastify-app && cd fastify-app
$ npm init -y
$ npm install --save fastify

ESMを有効にするため、package.jsonファイルを編集し、トップレベルにtype: moduleキーを追加します。

{
  "type": "module",
}

続いて、プロジェクトのルートディレクトリにあるserver.jsファイルに、Fastifyアプリケーションの基本構造を記述します。

// Import Fastify using ESM syntax
import Fastify from "fastify";

const fastify = Fastify({ logger: true });

// Defining a route
fastify.get("/", async (request, reply) => {
  return { hello: "world" };
});

async function startServer() {
  try {
    // Start the server
    const address = await fastify.listen({
      port: 3000,
      host: "localhost",
    });

    // Log the server start information using the address returned by Fastify
    // Fastify will already log the server start information by default
    // But here is an example of how you can log the server start information
    // fastify.log.info(`Server starting up at ${address}`);
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
}

startServer();

APIルートをFastifyプラグインにカプセル化する

Fastifyでは、コードを整理してカプセル化するためにプラグインの利用が推奨されています。関連するコードをまとめて管理できるため、アプリケーション内の異なる部分での競合を防ぎやすくなります。

「Hello World」ルートをFastifyプラグインにリファクタリングしましょう。

const fastify = require('fastify')({ logger: true });

const helloRoute = async (fastify, options) => {
  fastify.get('/', async (request, reply) => {
    return { hello: 'world' };
  });
};

fastify.register(helloRoute);

const start = async () => {
  try {
    await fastify.listen(3000);
    fastify.log.info(`server listening on ${fastify.server.address().port}`);
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};
start();

ルートのリクエストスキーマとレスポンススキーマを使う

Fastifyでは、JSON Schemaを使って受信リクエストを検証し、送信レスポンスの構造を定義できます。これにより、アプリケーションのデータ整合性を確保し、ドキュメントの作成も簡単になります。

「Hello World」ルートのリクエストスキーマとレスポンススキーマを定義する方法を紹介します。

const helloRoute = async (fastify, options) => {
  fastify.route({
    method: 'GET',
    url: '/',
    schema: {
      response: {
        200: {
          type: 'object',
          properties: {
            hello: { type: 'string' },
          },
        },
      },
    },
    handler: async (request, reply) => {
      return { hello: 'world' };
    },
  });
};

fastify.register(helloRoute);

これでFastifyは、「Hello World」ルートのレスポンスを指定したスキーマに照らして自動的に検証します。潜在的なバグを見つけやすくなり、アプリケーションが想定どおりに動作することを確認できます。

Fastifyアプリケーションの構築要素となるFastifyプラグイン

このセクションでは、4つの主要なFastifyプラグイン、@fastify/pino、@fastify/cors、@fastify/env、@fastify/swaggerと@fastify/swagger-uiを紹介します。これらのプラグインは、バックエンドNode.js APIを構築する際に、Fastifyアプリケーションの基盤となります。

Fastify向けPinoロガー

Pinoは、Node.js向けの非常に高性能なロガーです。Sentryなどのロギングシステムと連携できる独自のプラグインも備えています。

FastifyのデフォルトロガーにPinoが使われている点を活用できます。{ logger: true }を指定してFastifyを初期化すると、内部ではすでにPinoが使われています。ログレベルなど、Pino固有の設定を細かく制御したい場合は、loggerプロパティに設定オブジェクトを渡せます。

開発中は、より見やすく色鮮やかなログ出力が必要になることがあります。その場合はpino-prettyパッケージを使いましょう。まずpinoとpino-prettyをインストールします。

npm install --save pino pino-pretty

次に、以下のようにpino-prettyを設定します。

import pino from "pino";

const logger = pino({
  transport: {
    target: "pino-pretty",
    options: {
      colorize: true,
    },
  },
});

logger.info("Hello from pino-pretty");

const fastify = Fastify({
  logger: logger,
});

上記では、pinoでロガーを初期化し、ログ出力に色を付けるcolorizeオプションを指定して、トランスポート先にpino-prettyを設定しています。次に、カスタムのlogger変数をFastifyのloggerオプションに渡します。

これにより、ログ出力がより人間に読みやすい形式になり、内容を把握しやすくなります。

[12:13:43.499] INFO (95477): hello from pino-pretty
[12:13:43.517] INFO (95477): Server listening at http://[::1]:3000
[12:13:43.518] INFO (95477): Server listening at http://127.0.0.1:3000

FastifyでPinoを使うメリットは次のとおりです。

  • 高速なロギング:Pinoは、Node.jsで最速のロガーを目指して開発されています。

  • 柔軟なロガー:さまざまなログレベルに対応し、ログをJSON形式で出力できます。

Fastifyプラグイン@fastify/cors

@fastify/corsプラグインを使うと、FastifyアプリケーションでCORS(クロスオリジンリソース共有)を簡単に利用できます。

メリット:

  • 安全性:異なるオリジン間のデータの流れを管理できます。

  • カスタマイズ性:CORSオプションを設定するカスタム関数を定義できます。

@fastify/corsプラグインを追加して使う簡単な例を紹介します。

// after creating the fastify instance
// let's register the fastify cors plugin:
fastify.register(require('@fastify/cors'), { 
  origin: '*',
  methods: ['GET','POST', 'PUT', 'DELETE']
})

FastifyサーバーにcURLリクエストを送信して、CORS設定が機能しているかテストできます。

curl -i http://localhost:3000

レスポンスにAccess-Control-Allow-Originヘッダーが含まれていれば、CORS設定は想定どおりに動作しています。

HTTP/1.1 200 OK
access-control-allow-origin: *
content-type: application/json; charset=utf-8
content-length: 17
Date: Sun, 18 Feb 2024 10:20:15 GMT
Connection: keep-alive
Keep-Alive: timeout=72

Fastifyプラグイン@fastify/env

Node.js開発者が環境変数を使ってアプリケーションを設定するのは一般的です。こうした変数の管理には、.envファイルがよく使われます。

ここで役立つのが、Fastifyプラグインの@fastify/envです。設定の読み込みにはdotenvやNode.js組み込みの--env-fileも使えますが、このFastifyプラグインを使うと、Fastifyアプリケーションから環境変数の設定を直接扱えるうえ、設定の検証や型変換もできます。

@fastify/envプラグインを追加して使う簡単な例を紹介します。まずプラグインをインストールし、シンプルな.envファイルを作成します。

npm install --save @fastify/env
echo "PORT=3001" > .env

次にserver.jsのコードを更新し、新しいFastifyプラグインを登録します。

fastify.register(import('@fastify/env'), {
  dotenv: true,
  schema: {
    type: 'object',
    required: [ 'PORT' ],
    properties: {
      PORT: {
        type: 'string',
        default: 3000
      }
    }
  }
})

Fastifyプラグインの@fastify/envを登録すると、.envファイルで定義した設定にfastify.configオブジェクトからアクセスできます。ただし、そのまま使うことはできません。Fastifyのプラグインアーキテクチャに関する新しい概念を取り入れる必要があります。

Fastifyの非同期プラグインアーキテクチャでは、プラグインが適用される前に、Fastifyによるすべてのプラグインの初期化を待つ必要があります。これによりプラグインが正しく登録され、使用できる状態になります。この手順を省略すると、fastify.configオブジェクトにアクセスする際にエラーが発生します。

そこで、新しいstartServer()関数を次のように更新します。

try {
    // wait for all plugins to run 
    await fastify.ready();
    // Start the server
    const address = await fastify.listen({
      // now we can access the fastify.config.PORT configuration
      port: fastify.config.PORT,
      host: "localhost",
    });

@fastify/envプラグインを使うメリット:

  • 環境変数の検証:必須の環境変数が設定されていることを確認します。

  • 型変換:環境変数を必要な型に変換します。

  • 軽量な依存性注入:fastifyアプリケーションのインスタンスにアクセスできる場所ならどこでも、設定を簡単に共有できます。

@fastify/swaggerと@fastify/swagger-ui FastifyプラグインでバックエンドNode.js APIをドキュメント化する

@fastify/swaggerプラグインは、FastifyアプリケーションのSwaggerドキュメントページを生成して配信します。@fastify/swagger-uiはSwaggerドキュメント用の組み込みUIを提供するプラグインです。Webブラウザーからアクセスでき、Swagger UIのWebページはFastifyアプリケーション自体が配信します。

これまでと同様に、まずこれらのプラグインをFastifyアプリケーションに追加します。

npm install --save @fastify/swagger @fastify/swagger-ui

次に、server.jsファイルで2つのプラグインを登録します。

fastify.register(import('@fastify/swagger'), {
  routePrefix: '/documentation',
  swagger: {
    info: {
      title: 'Test swagger',
      description: 'testing the fastify swagger api',
      version: '0.1.0'
    },
    host: 'localhost',
    schemes: ['http'],
    consumes: ['application/json'],
    produces: ['application/json']
  },
  exposeRoute: true
})

fastify.register(import('@fastify/swagger-ui'))

Fastifyサーバーを再起動し、Webブラウザーでhttp://localhost:3001/documentationを開くと、生成されたSwagger UIドキュメントページを確認できます。これで、Fastifyアプリケーション向けのSwaggerドキュメントページが完成です。

Fastifyプラグインの@fastify/swaggerは、バックエンドのNode.js APIにSwaggerドキュメントを追加します。

セキュリティに関する注意:本番環境では、Swagger UIドキュメントページを認証と認可で保護することを検討してください。また、本番用のNode.jsサーバーでSwagger UIドキュメントページを配信することは避けましょう。

メリット:

  • APIドキュメントを簡単に作成:APIドキュメントを自動生成します。

  • カスタマイズ性:ドキュメントページをカスタマイズできます。

まとめ

ここまで見てきたように、Fastifyの活発なエコシステムには、バックエンドNode.js APIの開発を大きく向上させるさまざまなプラグインが揃っています。pinoとpino-prettyでロギング機能を改善し、@fastify/corsでブラウザーのCORSセキュリティを確保し、@fastify/envで環境変数を管理し、@fastify/swaggerと@fastify/swagger-uiでAPIをドキュメント化できます。これらのツールはワークフローを効率化し、パフォーマンスを高め、アプリケーションの品質を向上させるために設計されています。

Fastifyプラグインについて学んだことをまとめると、次のとおりです。

モジュール型開発を取り入れる:Fastifyのプラグインアーキテクチャは、モジュール型開発を促し、すっきりと保守しやすいコードベースの実現を後押しします。プラグインを統合することで、開発者は定型コードではなくビジネスロジックの記述に集中でき、パフォーマンスやスケーラビリティを損なうことなく、迅速に開発できます。

生産性とパフォーマンスの向上:それぞれのプラグインが特定の課題に対応し、Web開発でよくある問題を解決することで、堅牢で効率的かつ安全なAPIを構築できます。Fastifyに簡単に統合できるうえ、ログ機能の強化、クロスオリジンリソース共有、環境設定、APIドキュメント作成などのメリットも得られ、開発者の生産性とアプリケーションのパフォーマンスの向上に直結します。

さらなる活用を促す:ここでは重要なプラグインをいくつか紹介しましたが、Fastifyのエコシステムには、認証やデータベース連携、レート制限など、さまざまなニーズに対応するプラグインがほかにも豊富にあります。Fastifyのプラグインページを確認し、実際に試してみることで、API開発プロセスをさらに最適化し、強化する方法を見つけてみてください。

Fastifyプラグインを使ったバックエンドNode.js APIの上記すべての例について、ソースコード一式もご覧いただけます。