ウェブエンジニア問題集
第8章

Fastifyのhooks — 認証・ログ・前処理をライフサイクルに差し込む

4
この章の目次開く

hooksは、リクエスト処理の途中に共通処理を差し込む仕組みです。Expressのミドルウェアに近い役割もありますが、Fastifyでは「どのタイミングに差し込むか」が名前で分かれています。

fastify.addHook('preHandler', async (request, reply) => {
  if (!request.headers.authorization) {
    return reply.code(401).send({ error: 'UNAUTHORIZED' });
  }
});
js

addHook() の構文

構文: fastify.addHook(name, handler)

引数説明
namestringhook名。onRequestpreHandler など
handlerfunction指定したタイミングで実行される関数

戻り値: Fastifyインスタンス。登録処理として使います。

よく使うhooks

hook実行タイミング使い道
onRequestリクエストを受け取った直後リクエストID、簡単なログ
preValidationschema validationの前入力検証前に値を整える
preHandlerhandlerの直前認証、認可、DBから共通情報を読む
onSendレスポンス送信前ヘッダー追加、レスポンス加工
onResponseレスポンス送信後アクセスログ、計測
onErrorエラー発生時エラーログ

認証をpreHandlerに置く

管理APIだけに認証をかける例です。plugin内にhookを書くことで、影響範囲を /admin 配下に閉じ込めます。

async function adminRoutes(fastify) {
  fastify.addHook('preHandler', async (request, reply) => {
    const token = request.headers.authorization;
 
    if (token !== 'Bearer secret') {
      return reply.code(401).send({
        error: {
          code: 'UNAUTHORIZED',
          message: '認証が必要です',
        },
      });
    }
  });
 
  fastify.get('/stats', async () => {
    return { todos: 12 };
  });
}
 
fastify.register(adminRoutes, { prefix: '/admin' });
js

/admin/stats には認証がかかりますが、別pluginの /todos には影響しません。このスコープ制御は、pluginsとencapsulationの章で学んだ通りです。

route level hooks

特定のルートだけにhookを付けたい場合は、ルートのoptionsに書きます。

fastify.get('/me', {
  preHandler: async (request, reply) => {
    if (!request.headers.authorization) {
      return reply.code(401).send({ error: 'UNAUTHORIZED' });
    }
  },
}, async () => {
  return { id: 1, name: 'Taro' };
});
js

小さいAPIならこれでも十分です。複数ルートに同じ認証が必要になったら、plugin単位のhookへ移すと重複を減らせます。

学習者学習者

hookでレスポンスを返したら、handlerも実行されますか?

先生先生

reply.send()return reply.code(...).send(...) で返した場合、通常はそこで処理を終えます。認証失敗のようにhandlerへ進ませたくない場面で使います。

callback形式とasync形式を混ぜない

Fastifyのhooksには、done を呼ぶcallback形式と、Promiseを返すasync形式があります。新しく書くならasync形式に寄せるのが分かりやすいです。

fastify.addHook('preHandler', async (request) => {
  request.log.info('before handler');
});
js
async hookでは done callbackを使いません。asyncとcallbackを混ぜると、処理が二重に進むなど読みにくいバグの原因になります。

参考リンク

次章では、ここまでの知識を使って、Todo APIを小さなCRUDとして組み立てます。

Node.jsクイズに挑戦する非同期処理やHTTPリクエスト処理の知識をクイズで確認しよう