Merge pull request #15385 from mag123c/fix/fastify-adapter-middleware-init

fix(testing): auto-init fastify adapter for middleware registration
This commit is contained in:
Kamil Mysliwiec
2025-07-16 09:38:57 +02:00
committed by GitHub
2 changed files with 154 additions and 0 deletions

View File

@@ -0,0 +1,135 @@
import {
Controller,
Get,
Injectable,
MiddlewareConsumer,
Module,
NestModule,
} from '@nestjs/common';
import {
FastifyAdapter,
NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { Test } from '@nestjs/testing';
import { expect } from 'chai';
describe('Middleware before init (FastifyAdapter)', () => {
let app: NestFastifyApplication;
@Injectable()
class TestService {
getData(): string {
return 'test_data';
}
}
@Controller()
class TestController {
constructor(private readonly testService: TestService) {}
@Get('test')
test() {
return { data: this.testService.getData() };
}
@Get('health')
health() {
return { status: 'ok' };
}
}
@Module({
controllers: [TestController],
providers: [TestService],
})
class TestModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply((req, res, next) => {
res.setHeader('x-middleware', 'applied');
next();
})
.forRoutes('*');
}
}
describe('should queue middleware when registered before init', () => {
beforeEach(async () => {
const module = await Test.createTestingModule({
imports: [TestModule],
}).compile();
app = module.createNestApplication<NestFastifyApplication>(
new FastifyAdapter(),
);
// Register middleware before init - should be queued
app.use((req, res, next) => {
res.setHeader('x-global-middleware', 'applied');
next();
});
// Now init the app - queued middleware should be registered
await app.init();
await app.getHttpAdapter().getInstance().ready();
});
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();
});
});
describe('should work when app is initialized before middleware registration', () => {
beforeEach(async () => {
const module = await Test.createTestingModule({
imports: [TestModule],
}).compile();
app = module.createNestApplication<NestFastifyApplication>(
new FastifyAdapter(),
);
// Initialize app first
await app.init();
// Now middleware registration should work
app.use((req, res, next) => {
res.setHeader('x-global-middleware', 'applied');
next();
});
await app.getHttpAdapter().getInstance().ready();
});
it('should register middleware successfully after init', () => {
return app
.inject({
method: 'GET',
url: '/test',
})
.then(({ statusCode, payload }) => {
expect(statusCode).to.equal(200);
expect(JSON.parse(payload)).to.deep.equal({ data: 'test_data' });
});
});
afterEach(async () => {
await app.close();
});
});
});

View File

@@ -157,6 +157,7 @@ export class FastifyAdapter<
done: (err?: Error) => void, done: (err?: Error) => void,
) => void | Promise<void>; ) => void | Promise<void>;
private isMiddieRegistered: boolean; private isMiddieRegistered: boolean;
private pendingMiddlewares: Array<{ args: any[] }> = [];
private versioningOptions?: VersioningOptions; private versioningOptions?: VersioningOptions;
private readonly versionConstraint = { private readonly versionConstraint = {
name: 'version', name: 'version',
@@ -303,6 +304,14 @@ export class FastifyAdapter<
return; return;
} }
await this.registerMiddie(); 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; public listen(port: string | number, callback?: () => void): void;
@@ -717,6 +726,16 @@ export class FastifyAdapter<
return 'fastify'; return 'fastify';
} }
public use(...args: any[]) {
// Fastify requires @fastify/middie plugin to be registered before middleware can be used.
// If middie is not registered yet, we queue the middleware and register it later during init.
if (!this.isMiddieRegistered) {
this.pendingMiddlewares.push({ args });
return this;
}
return (this.instance.use as any)(...args);
}
protected registerWithPrefix( protected registerWithPrefix(
factory: factory:
| FastifyPluginCallback<any> | FastifyPluginCallback<any>