feat(platform-fastify): implement lazy middleware registration

This commit is contained in:
mag123c
2025-07-15 21:10:35 +09:00
parent d602f74c37
commit 07291c21aa
2 changed files with 41 additions and 27 deletions

View File

@@ -46,14 +46,14 @@ describe('Middleware before init (FastifyAdapter)', () => {
configure(consumer: MiddlewareConsumer) {
consumer
.apply((req, res, next) => {
req.headers['x-middleware'] = 'applied';
res.setHeader('x-middleware', 'applied');
next();
})
.forRoutes('*');
}
}
describe('should throw helpful error when middleware is registered before init', () => {
describe('should queue middleware when registered before init', () => {
beforeEach(async () => {
const module = await Test.createTestingModule({
imports: [TestModule],
@@ -63,23 +63,34 @@ describe('Middleware before init (FastifyAdapter)', () => {
new FastifyAdapter(),
);
// This should throw a helpful error message
let errorMessage = '';
try {
// Register middleware before init - should be queued
app.use((req, res, next) => {
req.headers['x-global-middleware'] = 'applied';
res.setHeader('x-global-middleware', 'applied');
next();
});
} catch (error) {
errorMessage = error.message;
}
expect(errorMessage).to.equal('this.instance.use is not a function');
// The helpful error message is logged, not thrown
// Now init the app - queued middleware should be registered
await app.init();
await app.getHttpAdapter().getInstance().ready();
});
it('should display clear error message', () => {
// Test is complete in beforeEach
it('should apply queued middleware after init', () => {
return app
.inject({
method: 'GET',
url: '/test',
})
.then(({ statusCode, payload, headers }) => {
expect(statusCode).to.equal(200);
expect(JSON.parse(payload)).to.deep.equal({ data: 'test_data' });
// Verify both module-level and global middleware were applied
expect(headers['x-middleware']).to.equal('applied');
expect(headers['x-global-middleware']).to.equal('applied');
});
});
afterEach(async () => {
await app.close();
});
});
@@ -98,7 +109,7 @@ describe('Middleware before init (FastifyAdapter)', () => {
// Now middleware registration should work
app.use((req, res, next) => {
req.headers['x-global-middleware'] = 'applied';
res.setHeader('x-global-middleware', 'applied');
next();
});

View File

@@ -146,6 +146,7 @@ export class FastifyAdapter<
private _isParserRegistered: boolean;
private isMiddieRegistered: boolean;
private pendingMiddlewares: Array<{ args: any[] }> = [];
private versioningOptions?: VersioningOptions;
private readonly versionConstraint = {
name: 'version',
@@ -256,6 +257,14 @@ export class FastifyAdapter<
return;
}
await this.registerMiddie();
// Register any pending middlewares that were added before init
if (this.pendingMiddlewares.length > 0) {
for (const { args } of this.pendingMiddlewares) {
(this.instance.use as any)(...args);
}
this.pendingMiddlewares = [];
}
}
public listen(port: string | number, callback?: () => void): void;
@@ -672,18 +681,12 @@ export class FastifyAdapter<
public use(...args: any[]) {
// Fastify requires @fastify/middie plugin to be registered before middleware can be used.
// Unlike Express, middleware registration in Fastify must happen after initialization.
// We provide a helpful error message to guide developers to call app.init() first.
// If middie is not registered yet, we queue the middleware and register it later during init.
if (!this.isMiddieRegistered) {
Logger.warn(
'Middleware registration requires the "@fastify/middie" plugin to be registered first. ' +
'Make sure to call app.init() before registering middleware with the Fastify adapter. ' +
'See https://github.com/nestjs/nest/issues/15310 for more details.',
FastifyAdapter.name,
);
throw new TypeError('this.instance.use is not a function');
this.pendingMiddlewares.push({ args });
return this;
}
return super.use(...args);
return (this.instance.use as any)(...args);
}
protected registerWithPrefix(