Fastifyでプラグインを作成し使用する方法

概要

Fastifyでは、プラグインを使用してアプリケーションに機能を追加したり、共有ロジックを再利用したりすることができます。

プラグインは、ミドルウェアやルートハンドラー、フック、デコレータなど、さまざまな機能を提供することができます。

ここでは、以下バージョンを使用した、Fastifyでプラグインを作成する方法を作成する方法を紹介します。

Fastify v4.0.0
nodejs v19.7.0

また、今回作成するコードは全て、以下に掲載しています。 https://github.com/wiblok/Fastify

プラグインの定義方法

プラグインで提供できる機能は以下のようなものがあります。

プラグインは、Fastifyアプリケーションに機能を追加するためのモジュールです。   実際にプラグイン化することが多い、使用例は以下の通りです。

  1. ルートハンドラを提供するプラグインを作成する方法   新しいエンドポイントや既存のエンドポイントに対するルートハンドラーを追加できます。 これにより、リクエストに対する処理やレスポンスの生成をカスタマイズすることができます。
  2. リクエストバリデーションを提供するプラグインを作成する方法
    リクエストのバリデーションや検証を行うための機能を提供できます。入力データの妥当性を検証し、必要な形式や制約に合致するかを確認することができます。
  3. プリミティブの追加するプラグインを作成する方法
    Fastifyに新しいプリミティブ(例: デコレータ)を追加することができます。これにより、アプリケーション全体で再利用可能な処理や機能を追加できます。
  4. ミドルウェアをカプセル化したプラグインを作成する方法
    プラグインは、リクエスト処理の前後にフック(middleware)を追加できます。これにより、リクエストの前処理や後処理を行うことができます。例えば、認証やログ記録などの処理をフックに追加することができます。

ルートハンドラを提供するプラグインを作成する方法

route.js

// プラグイン関数の定義
function myPlugin(fastify, options, done) {
  // プラグインの機能を実装するコード

  // ルートハンドラーの追加
  fastify.get('/hello', (request, reply) => {
    reply.send({ message: 'Hello from the plugin!' });
  });

  done(); // プラグインの登録完了を示す
}

module.exports = myPlugin;

解説

  • プラグイン関数の定義
function myPlugin(fastify, options, done) {
  // プラグインの機能を実装するコード

  // ルートハンドラーの追加
  fastify.get('/hello', (request, reply) => {
    reply.send({ message: 'Hello from the plugin!' });
  });

  done(); // プラグインの登録完了を示す
}

Fastifyのプラグインは、Fastifyインスタンス、オプション、そして登録完了を示すコールバック関数を引数に取る関数として定義されます。このプラグインでは、/helloというパスへのGETリクエストをハンドリングするルートハンドラーを追加しています。リクエストが来ると、“Hello from the plugin!“というメッセージを含むオブジェクトをレスポンスとして返します。プラグインの登録が完了したら、done()を呼び出してFastifyに通知します。

  • プラグインのエクスポート
module.exports = myPlugin;

プラグイン関数myPluginをエクスポートします。これにより、他のファイルからこのプラグインをrequireして使用することができます。

route-main.js*

const fastify = require('fastify')();
const myPlugin = require('./my-plugin');

const options = {}
fastify.register(myPlugin, options);

fastify.listen({ port: 3000 }, (err) => {
  if (err) {
    console.error(err);
    process.exit(1);
  }
  console.log('Server is running on port 3000');
});

解説

  • Fastifyのインスタンスを作成
const fastify = require('fastify')();

Fastifyモジュールをインポートし、そのインスタンスを作成します。

  • プラグインのインポート
const myPlugin = require('./my-plugin');

my-plugin.jsという名前のファイルからプラグインをインポートします。このファイルは同じディレクトリに存在する必要があります。

  • プラグインの登録
const options = {}
fastify.register(myPlugin, options);

Fastifyインスタンスにプラグインを登録します。このとき、プラグインに渡すオプションを指定することができます。この例では空のオプションオブジェクトを渡しています。

  • サーバーの起動
fastify.listen({ port: 3000 }, (err) => {
  if (err) {
    console.error(err);
    process.exit(1);
  }
  console.log('Server is running on port 3000');
});

Fastifyのインスタンスをポート3000でリッスンさせ、サーバーを起動します。もし何らかのエラーが発生した場合は、エラーメッセージをコンソールに出力し、プロセスを終了します。エラーがなければ、“Server is running on port 3000"というメッセージをコンソールに出力します。

テスト

curl http://localhost:3000

リクエストバリデーションを提供するプラグインを作成する方法

validation-plugin

// プラグイン関数の定義
function validationPlugin(fastify, options, done) {
  // スキーマの定義
  const schema = {
    type: "object",
    properties: {
      name: { type: "string" },
      age: { type: "number", minimum: 0 },
    },
    required: ["name", "age"],
  };

  // プラグインが提供するバリデーション関数
  function validateRequest(request, reply, done) {
    const validationResult = fastify.schemas.validate(schema, request.body);
    if (validationResult.error) {
      // バリデーションエラーが発生した場合
      reply.code(400).send({ error: validationResult.error.message });
    } else {
      // バリデーションが成功した場合
      request.body = validationResult.value;
      done();
    }
  }

  // プラグインの登録完了を示す
  done();

  // プラグインの機能をオブジェクトとして返す
  return {
    validateRequest: validateRequest,
  };
}

module.exports = validationPlugin;

解説

  • プラグイン関数の定義
function validationPlugin(fastify, options, done) {
  // スキーマの定義
  const schema = {
    type: "object",
    properties: {
      name: { type: "string" },
      age: { type: "number", minimum: 0 },
    },
    required: ["name", "age"],
  };

Fastifyのプラグインは、Fastifyインスタンス、オプション、そして登録完了を示すコールバック関数を引数に取る関数として定義されます。このプラグインでは、バリデーションスキーマを定義しています。スキーマは、オブジェクトであり、nameプロパティが文字列、ageプロパティが0以上の数値であることを要求します。これらのプロパティは必須です。

  • プラグインが提供するバリデーション関数
  function validateRequest(request, reply, done) {
    const validationResult = fastify.schemas.validate(schema, request.body);
    if (validationResult.error) {
      // バリデーションエラーが発生した場合
      reply.code(400).send({ error: validationResult.error.message });
    } else {
      // バリデーションが成功した場合
      request.body = validationResult.value;
      done();
    }
  }

プラグインは、リクエストボディのバリデーションを行うvalidateRequest関数を提供します。バリデーションは、Fastifyのschemas.validateメソッドを使用して行います。バリデーションエラーが発生した場合、400ステータスコードとエラーメッセージをレスポンスとして返します。バリデーションが成功した場合、リクエストボディをバリデーション後の値に更新し、次のハンドラーに処理を進めます。

  • プラグインの登録完了を示す
  done();

プラグインの登録が完了したら、done()を呼び出してFastifyに通知します。

  • プラグインの機能をオブジェクトとして返す
  return {
    validateRequest: validateRequest,
  };
}

プラグインは、提供する機能をオブジェクトとして返します。この例では、validateRequest関数を返しています。

  • プラグインのエクスポート
module.exports = validationPlugin;

プラグイン関数validationPluginをエクスポートします。これにより、他のファイルからこのプラグインをrequireして使用することができます。

validation-main

const fastify = require("fastify")();
const validationPlugin = require("./validation-plugin");

// プラグインの登録
fastify.register(validationPlugin);

// ルートハンドラの定義
fastify.post(
  "/hello",
  { preValidation: fastify.validateRequest },
  (request, reply) => {
    const { name, age } = request.body;
    reply.send({ message: `Hello ${name}! Your age is ${age}.` });
  }
);

// サーバーの起動
fastify.listen({ port: 3000 }, (err) => {
  if (err) {
    console.error(err);
    process.exit(1);
  }
  console.log("Server is running on port 3000");
});

解説

  • Fastifyのインスタンスを作成
const fastify = require("fastify")();

Fastifyモジュールをインポートし、そのインスタンスを作成します。

  • プラグインのインポート
const validationPlugin = require("./validation-plugin");

validation-plugin.jsという名前のファイルからプラグインをインポートします。このファイルは同じディレクトリに存在する必要があります。

  • プラグインの登録
fastify.register(validationPlugin);

Fastifyインスタンスにプラグインを登録します。

  • ルートハンドラの定義
fastify.post(
  "/hello",
  { preValidation: fastify.validateRequest },
  (request, reply) => {
    const { name, age } = request.body;
    reply.send({ message: `Hello ${name}! Your age is ${age}.` });
  }
);

/helloというパスへのPOSTリクエストをハンドリングするルートハンドラーを定義します。このハンドラーは、プラグインが提供するvalidateRequest関数をpreValidationフックとして使用します。これにより、ハンドラーが実行される前にリクエストボディのバリデーションが行われます。バリデーションが成功した場合、リクエストボディからnameageを取り出し、それらを含むメッセージをレスポンスとして返します。

  • サーバーの起動
fastify.listen({ port: 3000 }, (err) => {
  if (err) {
    console.error(err);
    process.exit(1);
  }
  console.log("Server is running on port 3000");
});

Fastifyのインスタンスをポート3000でリッスンさせ、サーバーを起動します。もし何らかのエラーが発生した場合は、エラーメッセージをコンソールに出力し、プロセスを終了します。エラーがなければ、“Server is running on port 3000"というメッセージをコンソールに出力します。

テスト

# 正常なリクエストのテスト
curl -X POST -H "Content-Type: application/json" -d '{"name": "John", "age": 25}' http://localhost:3000/hello
# バリデーションエラーが発生するリクエストのテスト
curl -X POST -H "Content-Type: application/json" -d '{"name": "John"}' http://localhost:3000/hello

このコードでは、Fastifyを使用してプラグインを作成し、リクエストのバリデーションを行う機能を提供しています。プラグインは、Fastifyインスタンスに登録され、指定したパスに対してリクエストが送信された際にバリデーションが実行されます。バリデーションが成功した場合はハンドラーが実行され、適切なレスポンスが返されます。

プラグインをFstifyにデコレートする方法

decorator-plugin.js

// プラグイン関数の定義
function validationPlugin(fastify, options, done) {
  // スキーマの定義
  const schema = {
    type: "object",
    properties: {
      name: { type: "string" },
      age: { type: "number", minimum: 0 },
    },
    required: ["name", "age"],
  };

  // プラグインが提供するバリデーション関数
  function validateRequest(request, reply, done) {
    console.log("===");
    const validationResult = fastify.validate(schema, request.body);
    if (validationResult.error) {
      // バリデーションエラーが発生した場合
      reply.code(400).send({ error: validationResult.error.message });
    } else {
      // バリデーションが成功した場合
      request.validatedBody = validationResult.value;
      done();
    }
  }

  // プラグインの登録完了を示す
  done();

  // スキーマの提供関数をデコレート
  fastify.decorate("validateRequest", validateRequest);
}

module.exports = validationPlugin;

解説

  • プラグイン関数の定義
function validationPlugin(fastify, options, done) {
  // スキーマの定義
  const schema = {
    type: "object",
    properties: {
      name: { type: "string" },
      age: { type: "number", minimum: 0 },
    },
    required: ["name", "age"],
  };

Fastifyのプラグインは、Fastifyインスタンス、オプション、そして登録完了を示すコールバック関数を引数に取る関数として定義されます。このプラグインでは、バリデーションスキーマを定義しています。スキーマは、オブジェクトであり、nameプロパティが文字列、ageプロパティが0以上の数値であることを要求します。これらのプロパティは必須です。

  • プラグインが提供するバリデーション関数
function validateRequest(request, reply, done) {
  console.log("===");
  const validationResult = fastify.validate(schema, request.body);
  if (validationResult.error) {
    // バリデーションエラーが発生した場合
    reply.code(400).send({ error: validationResult.error.message });
  } else {
    // バリデーションが成功した場合
    request.validatedBody = validationResult.value;
    done();
  }
}

プラグインは、リクエストボディのバリデーションを行うvalidateRequest関数を提供します。バリデーションは、Fastifyのschemas.validateメソッドを使用して行います。バリデーションエラーが発生した場合、400ステータスコードとエラーメッセージをレスポンスとして返します。バリデーションが成功した場合、リクエストオブジェクトにvalidatedBodyプロパティを追加し、その値をバリデーション後の値に設定します。その後、次のハンドラーに処理を進めます。

  • プラグインの登録完了を示す
done();

プラグインの登録が完了したら、done()を呼び出してFastifyに通知します。

  • スキーマの提供関数をデコレート
fastify.decorate("validateRequest", validateRequest);

FastifyインスタンスにvalidateRequest関数をデコレート(追加)します。これにより、Fastifyインスタンスから直接この関数を呼び出すことができます。

  • プラグインのエクスポート
module.exports = validationPlugin;

プラグイン関数validationPluginをエクスポートします。これにより、他のファイルからこのプラグインをインポートして使用することができます。

decorator-main.js

const fastify = require("fastify")();

// プラグインの読み込みと登録
const validationPlugin = require("./decorator-plugin");
fastify.register(validationPlugin);

// ルートハンドラの定義
fastify.post(
  "/hello",
  { preValidation: fastify.validateRequest },
  (request, reply) => {
    const { name, age } = request.body;
    reply.send({ message: `Hello ${name}! Your age is ${age}.` });
  }
);

// サーバーの起動
fastify.listen({ port: 3000 }, (err) => {
  if (err) {
    console.error(err);
    process.exit(1);
  }
  console.log("Server is running on port 3000");
});

解説

  • Fastifyのインスタンスを作成
const fastify = require("fastify")();

Fastifyモジュールをインポートし、そのインスタンスを作成します。

  • プラグインのインポートと登録
const validationPlugin = require("./decorator-plugin");
fastify.register(validationPlugin);

decorator-plugin.jsという名前のファイルからプラグインをインポートし、Fastifyインスタンスに登録します。このファイルは同じディレクトリに存在する必要があります。

  • ルートハンドラの定義
fastify.post(
  "/hello",
  { preValidation: fastify.validateRequest },
  (request, reply) => {
    const { name, age } = request.body;
    reply.send({ message: `Hello ${name}! Your age is ${age}.` });
  }
);

/helloというパスへのPOSTリクエストをハンドリングするルートハンドラーを定義します。このハンドラーは、プラグインが提供するvalidateRequest関数をpreValidationフックとして使用します。これにより、ハンドラーが実行される前にリクエストボディのバリデーションが行われます。バリデーションが成功した場合、リクエストボディからnameageを取り出し、それらを含むメッセージをレスポンスとして返します。

  • サーバーの起動
fastify.listen({ port: 3000 }, (err) => {
  if (err) {
    console.error(err);
    process.exit(1);
  }
  console.log("Server is running on port 3000");
});

Fastifyのインスタンスをポート3000でリッスンさせ、サーバーを起動します。もし何らかのエラーが発生した場合は、エラーメッセージをコンソールに出力し、プロセスを終了します。エラーがなければ、“Server is running on port 3000"というメッセージをコンソールに出力します。

テスト

# 正常なリクエストのテスト
curl -X POST -H "Content-Type: application/json" -d '{"name": "John", "age": 25}' http://localhost:3000/hello
# バリデーションエラーが発生するリクエストのテスト
curl -X POST -H "Content-Type: application/json" -d '{"name": "John"}' http://localhost:3000/hello

このコードでは、Fastifyのプラグインを使用してリクエストのバリデーション機能を追加し、デコレータパターンを活用してバリデーション関数をFastifyインスタンスに追加しています。 これにより、ルートハンドラ内で簡単にバリデーション機能を利用できるようになります。

ミドルウェアをカプセル化したプラグインを作成する方法

capsule.js

const fastify = require('fastify')();

const myPlugin = (fastify, options, done) => {
  function myMiddleware(request, reply, next) {
    // ミドルウェアの処理
    console.log('ミドルウェアの実行');
    next();
  }

  fastify.addHook('preHandler', myMiddleware);

  done();
};

fastify.register(myPlugin);

fastify.get('/', (request, reply) => {
  reply.send({ message: 'Hello, World!' });
});

fastify.listen({ port: 3000 }, (err) => {
  if (err) {
    console.error(err);
    process.exit(1);
  }
  console.log('Server is running on port 3000');
});

解説

  • Fastifyのインスタンスを作成
const fastify = require("fastify")();

Fastifyモジュールをインポートし、そのインスタンスを作成します。

  • プラグインの定義
const myPlugin = (fastify, options, done) => {
  function myMiddleware(request, reply, next) {
    // ミドルウェアの処理
    console.log('ミドルウェアの実行');
    next();
  }

  fastify.addHook('preHandler', myMiddleware);

  done();
};

プラグインを定義します。このプラグインはミドルウェア関数myMiddlewareを定義し、それをpreHandlerフックとしてFastifyインスタンスに追加します。myMiddleware関数は、ルートハンドラーが実行される前に呼び出され、コンソールにメッセージを出力します。

  • プラグインの登録
fastify.register(myPlugin);

Fastifyインスタンスにプラグインを登録します。

  • ルートハンドラの定義
fastify.get('/', (request, reply) => {
  reply.send({ message: 'Hello, World!' });
});

/というパスへのGETリクエストをハンドリングするルートハンドラーを定義します。リクエストが来ると、{ message: 'Hello, World!' }というオブジェクトをレスポンスとして返します。

  • サーバーの起動
fastify.listen({ port: 3000 }, (err) => {
  if (err) {
    console.error(err);
    process.exit(1);
  }
  console.log('Server is running on port 3000');
});

Fastifyのインスタンスをポート3000でリッスンさせ、サーバーを起動します。もし何らかのエラーが発生した場合は、エラーメッセージをコンソールに出力し、プロセスを終了します。エラーがなければ、“Server is running on port 3000"というメッセージをコンソールに出力します。

テスト

curl -X GET http://localhost:3000/

このコードでは、Fastifyを使用してサーバーを作成し、プラグインを使用してミドルウェアを定義しています。プラグインは、Fastifyインスタンスに機能を追加するための拡張性の高い方法です。ミドルウェアは、リクエストがハンドラーに到達する前に実行され、ミドルウェアの処理が実行されることを示しています。