I have a Nestjs
app with Fastify
. I want to apply the fastifySession
middleware using the MiddlewareConsumer
. Usually, it works like below:
configure(consumer: MiddlewareConsumer) {
consumer
.apply(
fastifySession,
)
.forRoutes({
path: '*',
method: RequestMethod.ALL,
});
}
}
The problem is that fastifySession
needs an options
object. In a regular app, it is called using the register
method like below:
app.register(fastifySession, {
secret: '',
cookie: {
secure: false,
domain: 'localhost',
},
store: new SessionStore(new SessionService()),
});
I don't want to use the register
method in main.ts
as I want to make use of the Nestjs dependency injection
. So, I need to apply the middleware in the AppModule. Is there a way to do this?
UPDATE
I thought I can develop a Nestjs middleware to register the fastify plugins that I need.
I created this middleware:
@Injectable()
class FastifySession implements NestMiddleware {
private options;
private fastifyPassport;
constructor(
private adapterHost: HttpAdapterHost,
private sessionStore: SessionStore,
private userService: UserService,
) {
this.fastifyPassport = new Authenticator();
this.options = {
cookie: {
secure: false,
maxAge: 50000,
path: '/',
httpOnly: true,
sameSite: false,
domain: 'localhost',
},
store: this.sessionStore,
};
}
use(req: any, res: any, next: (error?: any) => void) {
const httpAdapter = this.adapterHost.httpAdapter;
const instance = httpAdapter.getInstance();
instance.register(fastifyCookie);
instance.register(fastifySession, this.options);
instance.register(this.fastifyPassport.initialize());
instance.register(this.fastifyPassport.secureSession());
this.fastifyPassport.registerUserSerializer(async (user: User, request) => {
console.log(user.id);
return user.id;
});
this.fastifyPassport.registerUserDeserializer(async (id, request) => {
const user = await this.userService.getUser(+id);
console.log('user ', user);
return user;
});
next();
}
}
I added the middleware to my AppModule
export class AppModule implements NestModule {
constructor() {
}
configure(consumer: MiddlewareConsumer) {
consumer
.apply(FastifySession)
.forRoutes({
path: '*',
method: RequestMethod.ALL,
});
}
}
But I get his error
ERROR [ExceptionsHandler] Root plugin has already booted
AvvioError: Root plugin has already booted
I came across this GitHub Issue
https://github.com/nestjs/nest/issues/1462
As stated in the GitHub issue, I think It is not possible to register Fastify plugins outside main.ts.
I will appreciate anyone helping me! or at least guide me in the right direction.
To register a plugin inside a module, I would use onModuleInit
and a module that injects the HttpAdapterHost
. Something like this:
@Module(moduleMetadata)
export class AppModule implements OnMoudleInit {
constructor(private readonly httpAdapterHost: HttpAdapterHost) {}
async onModuleInit() {
const adapterInstance = this.httpAdapterHost.httpAdapter.getInstance();
await adapterInstance.register(fastifySession, fastifySessionOptions)
}
}
This way, the adapter registers the middleware at application startup and you don't accidentally end up registering it multiple times on each route call (use
will be called on each matching route and you end up calling instance.register()
a lot with the current code in the question)