背景
認証などをサーバ側で行なった際、ユーザを特定させると思うのですが、その特定したユーザ情報をrequestにいい感じに(Typescriptの怒られない形)で実現したいなと思い調べてみました。
ちなみに、fastifyのversionはv3系です。
結論を先に書いとくと、decorateRequestを使うといい感じにできました。
やり方
認証機能などを実装する際、fastifyのhooksを用いて下記のように書くと思うのですが、そうするとTSに怒られる。
fastify.addHook('onRequest', (request, reply, next) => {
// TSにrequestにuserIdなんてプロパティないわと怒られる
request.userId = userId;
next()
})
かと言って下記の様にしちゃうと、どこからでも上書き出来てしまって嫌だな〜と思っていた。
できれば、setterを使いたい。
# @types/fastify/index.d.ts
import { FastifyInstance } from 'fastify';
declare module 'fastify' {
export interface FastifyRequest {
userId: number; // ここをreadonly userId: numberにしたい
}
}
そこで、色々調べているとdecorateRequestなるものを使えば、requestに新しいプロパティやメソッドを生やせることがわかったので使ってみることにしました。
定義はこんな感じ。
const server = fastify();
...
server.decorateRequest('setUserInfo', function (this: FastifyInstance, userId: number) {
this.userId = userId;
});
...
export default server;
すると、addHook時などで、request.setUserInfoでアクセスできる様になってる!!
server.addHook('onRequest', async (request: FastifyRequest) => {
// 認証・認可の処理をして、そのuserIdを取得する
const userId = await auth(request);
request.setUserInfo(userId);
});
型定義はこんな感じ。
# @types/fastify/index.d.ts
import { FastifyInstance } from 'fastify';
export interface setUserInfo {
(userId: number): void;
}
declare module 'fastify' {
export interface FastifyInstance {
userId: number;
}
export interface FastifyRequest {
readonly userId: number;
setUserInfo: setUserInfo;
}
}
実際、APIなどの処理部分でrequest.userIdってやると値が取れるようにになっている。
interface IQuerystring {
name: string;
}
export const handler = async (request: FastifyRequest<{ Querystring: IQuerystring }>, reply: FastifyReply) => {
...
// 認証されたユーザのuserIdがコンソールに出力される。
console.log(request.userId);
...
};
補足
なお、別に最初の
request.userId = userId;
このケースでも型定義さえ拡張しとけば普通に動くけど、公式に
Note: The usage of decorateReply and decorateRequest is optional in this case but will allow Fastify to optimize for performance
って書いてあるから、上記の様に書くなら
server.decorateRequest('userId', null);
みたいな感じで定義しておいた方がいいかも。
結論
fastifyRequestにメソッドとかプロパティをはやしたいなら、decorateRequestを使った方が良さげ。
なお、reply周りをいじりたいときは、decorateReplyがあるみたいですね。