在NestJS中通过MiddlewareConsumer使用fastifySession中间件遇阻求助
解决方案:NestJS + Fastify 中通过DI注册fastifySession及Passport插件
错误原因分析
你遇到的Root plugin has already booted错误,核心原因是Fastify插件必须在实例启动(调用listen)前完成注册,而Nest的Middleware是在请求处理阶段执行的——此时Fastify已经启动完成,无法再动态注册新插件。
正确实现方式
方案一:利用OnModuleInit钩子(推荐)
通过Nest的OnModuleInit生命周期钩子,在模块初始化完成、应用启动前注册Fastify插件,同时可以直接注入依赖服务(满足DI需求)。
import { Module, OnModuleInit, HttpAdapterHost } from '@nestjs/common'; import { fastifyCookie } from '@fastify/cookie'; import fastifySession from '@fastify/session'; import { Authenticator } from '@fastify/passport'; import { SessionStore } from './session.store'; import { UserService } from './user.service'; import { User } from './user.entity'; @Module({ providers: [SessionStore, UserService], }) export class AppModule implements OnModuleInit { private fastifyPassport: Authenticator; constructor( private readonly httpAdapterHost: HttpAdapterHost, private readonly sessionStore: SessionStore, private readonly userService: UserService, ) { this.fastifyPassport = new Authenticator(); } async onModuleInit() { const httpAdapter = this.httpAdapterHost.httpAdapter; const fastifyInstance = httpAdapter.getInstance(); // 先注册session依赖的cookie插件 await fastifyInstance.register(fastifyCookie); // 注册session插件,使用DI注入的store await fastifyInstance.register(fastifySession, { secret: process.env.SESSION_SECRET || 'your-strong-secret', // 建议用环境变量 cookie: { secure: false, maxAge: 50000, path: '/', httpOnly: true, sameSite: false, domain: 'localhost', }, store: this.sessionStore, }); // 初始化Passport await fastifyInstance.register(this.fastifyPassport.initialize()); await fastifyInstance.register(this.fastifyPassport.secureSession()); // 配置用户序列化/反序列化,依赖注入的UserService this.fastifyPassport.registerUserSerializer(async (user: User) => { return user.id; }); this.fastifyPassport.registerUserDeserializer(async (id: string) => { return this.userService.getUser(+id); }); } }
方案二:自定义Fastify Adapter(适合深度定制场景)
如果需要对Fastify实例做更多初始化配置,可以自定义Adapter,在实例创建阶段完成插件注册。
1. 自定义Adapter
import { FastifyAdapter } from '@nestjs/platform-fastify'; import fastifyCookie from '@fastify/cookie'; import fastifySession from '@fastify/session'; import { Authenticator } from '@fastify/passport'; import { SessionStore } from './session.store'; import { UserService } from './user.service'; import { User } from './user.entity'; export class CustomFastifyAdapter extends FastifyAdapter { constructor( private readonly sessionStore: SessionStore, private readonly userService: UserService, ) { super(); } async init(): Promise<void> { await super.init(); const fastifyInstance = this.getInstance(); const fastifyPassport = new Authenticator(); await fastifyInstance.register(fastifyCookie); await fastifyInstance.register(fastifySession, { secret: process.env.SESSION_SECRET || 'your-strong-secret', cookie: { secure: false, maxAge: 50000, path: '/', httpOnly: true, sameSite: false, domain: 'localhost', }, store: this.sessionStore, }); await fastifyInstance.register(fastifyPassport.initialize()); await fastifyInstance.register(fastifyPassport.secureSession()); fastifyPassport.registerUserSerializer(async (user: User) => { return user.id; }); fastifyPassport.registerUserDeserializer(async (id: string) => { return this.userService.getUser(+id); }); } }
2. 在main.ts中使用自定义Adapter
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { CustomFastifyAdapter } from './custom-fastify.adapter'; async function bootstrap() { const app = await NestFactory.create(AppModule); // 从DI容器获取依赖服务 const sessionStore = app.get('SessionStore'); const userService = app.get('UserService'); // 替换默认Adapter const adapter = new CustomFastifyAdapter(sessionStore, userService); app.useHttpAdapter(adapter); await app.listen(3000); } bootstrap();
关键说明
- 两种方案都保证了Fastify插件在实例启动前完成注册,避开了"Root plugin已启动"的限制。
- 均支持Nest的依赖注入,可以直接使用
SessionStore、UserService等服务,无需手动实例化。
内容的提问来源于stack exchange,提问作者user12898141
相关产品推荐
相关产品推荐

