メインコンテンツまでスキップ

AdditionalRequest

controller.ts / hooks.ts
export type AdditionalRequest = Pick<FastifyRequest, 'cookies'> & {
setCookie: (...args: Parameters<FastifyReply['setCookie']>) => void;
token: string;
};

AdditionalRequest を使用することで、FastifyRequest または Request 型を拡張できます。 これにより、数々のフックのとハンドラーとの間で情報を保持できます。

Frourio は controller.tshooks.ts に定義されている AdditionalRequest を用いて自動的にハンドラーとフックの引数を拡張します。

  • コントローラーレベル: 現在のエンドポイントにのみ影響します。controller.ts に定義されます。
  • ディレクトリレベル: 現在のエンドポイントとその下にあるエンドポイント全てに影響します。hooks.ts に定義されます。
備考

このページの例は Fastify で書かれていますが、Express でも同じ機能が有効です。

Express での例を追加するプルリクエストも歓迎しています!

フックとコントローラーの橋渡し

フックとハンドラーとの間で値を共有するには、AdditionalRequest で拡張して値を格納します。

controller.ts
import { defineController, defineHooks } from './$relay';

export type AdditionalRequest = {
foo: string;
};

export const hooks = defineHooks(() => ({
preHandler: (req, reply, done) => {
req.foo = 'Stored in hooks!';
done();
},
}));

export default defineController(() => ({
get: (req) => {
return { status: 200, body: req.foo };
},
}));

プラグインによる型の拡張の反映

ハンドラーの第一引数は @fastify/cookie などの プラグインによる型の拡張を反映しません。これは FastifyRequest ではなく、frourio によって作られたものだからです。

拡張された内容にアクセスしたい場合、AdditinalRequest で拡張しフックで値を格納する必要があります。

controller.ts
import { defineController, defineHooks } from './$relay';
import { FastifyReply, FastifyRequest } from 'fastify';

export type AdditionalRequest = Pick<FastifyRequest, 'cookies'> & {
setCookie: (...args: Parameters<FastifyReply['setCookie']>) => void;
};

export const hooks = defineHooks(() => ({
preHandler: (req, reply, done) => {
Object.assign(req, {
setCookie: (...args: Parameters<FastifyReply['setCookie']>) => void reply.setCookie(...args),
});
done();
},
}));

export default defineController(() => ({
post: async ({ body: { token }, setCookie }) => {
const expiresIn = 60 * 60 * 24 * 5 * 1000;
const cookie = await getCookie(token, { expiresIn }); // some function that returns a cookie
setCookie('session', cookie, {
maxAge: expiresIn,
httpOnly: true,
secure: process.env.NODE_ENV !== 'development',
path: '/',
});
return { status: 200 };
},
delete: async ({ setCookie, cookies }) => {
const sessionId = cookies.session ?? '';
if (sessionId) await revokeSession(sessionId); // some function that revokes a session
setCookie('session', '', { maxAge: 0, path: '/' });
return { status: 200 };
},
}));