Skip to content

Commit eb0ab9e

Browse files
author
Jordan BAO
committed
feat(fastify): implement fallthrough option for fastify adapter
1 parent 4a3c08c commit eb0ab9e

File tree

2 files changed

+179
-68
lines changed

2 files changed

+179
-68
lines changed

lib/loaders/fastify.loader.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,25 @@ export class FastifyLoader extends AbstractLoader {
4141
}
4242

4343
const renderFn = (req: any, res: any) => {
44-
fs.stat(indexFilePath, (err) => {
44+
if (options.serveStaticOptions?.fallthrough) {
45+
const error = new NotFoundException();
46+
res.status(error.getStatus()).send(error.getResponse());
47+
return;
48+
}
49+
50+
fs.stat(indexFilePath, (err, stat) => {
4551
if (err) {
4652
const error = new NotFoundException();
4753
res.status(error.getStatus()).send(error.getResponse());
4854
return;
4955
}
50-
56+
5157
const stream = fs.createReadStream(indexFilePath);
52-
58+
5359
if (options.serveStaticOptions?.setHeaders) {
54-
const stat = fs.statSync(indexFilePath);
5560
options.serveStaticOptions.setHeaders(res, indexFilePath, stat);
5661
}
57-
62+
5863
res.type('text/html').send(stream);
5964
});
6065
};

tests/e2e/fastify-adapter.e2e-spec.ts

Lines changed: 169 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -9,78 +9,184 @@ import { NoopLogger } from '../utils/noop-logger';
99
describe('Fastify adapter', () => {
1010
let app: NestFastifyApplication;
1111

12-
beforeAll(async () => {
13-
app = await NestFactory.create(
14-
AppModule.withDefaults(),
15-
new FastifyAdapter(),
16-
{
17-
logger: new NoopLogger()
18-
}
19-
);
20-
app.setGlobalPrefix('api');
21-
22-
await app.init();
23-
await app.getHttpAdapter().getInstance().ready();
24-
});
12+
describe('when "fallthrough" option is set to "true"', () => {
13+
beforeAll(async () => {
14+
app = await NestFactory.create(
15+
AppModule.withFallthrough(),
16+
new FastifyAdapter(),
17+
{
18+
logger: new NoopLogger()
19+
}
20+
);
21+
app.setGlobalPrefix('api');
2522

26-
describe('GET /api', () => {
27-
it('should return "Hello, world!"', async () => {
28-
return app
29-
.inject({
30-
method: 'GET',
31-
url: '/api'
32-
})
33-
.then((result) => {
34-
expect(result.statusCode).toEqual(200);
35-
expect(result.payload).toEqual('Hello, world!');
36-
});
23+
await app.init();
24+
await app.getHttpAdapter().getInstance().ready();
3725
});
38-
});
3926

40-
describe('GET /', () => {
41-
it('should return HTML file', async () => {
42-
return app
43-
.inject({
44-
method: 'GET',
45-
url: '/'
46-
})
47-
.then((result) => {
48-
expect(result.statusCode).toEqual(200);
49-
expect(result.headers['content-type']).toMatch(/html/);
50-
expect(result.payload).toContain('Static website');
51-
});
27+
describe('GET /api', () => {
28+
it('should return "Hello, world!"', async () => {
29+
return app
30+
.inject({
31+
method: 'GET',
32+
url: '/api'
33+
})
34+
.then((result) => {
35+
expect(result.statusCode).toEqual(200);
36+
expect(result.payload).toEqual('Hello, world!');
37+
});
38+
});
5239
});
53-
});
5440

55-
describe('GET /index.html', () => {
56-
it('should return index page', async () => {
57-
return app
58-
.inject({
59-
method: 'GET',
60-
url: '/index.html'
61-
})
62-
.then((result) => {
63-
expect(result.statusCode).toEqual(200);
64-
expect(result.payload).toContain('Static website');
65-
});
41+
describe('GET /', () => {
42+
it('should return HTML file', async () => {
43+
return app
44+
.inject({
45+
method: 'GET',
46+
url: '/'
47+
})
48+
.then((result) => {
49+
expect(result.statusCode).toEqual(200);
50+
expect(result.headers['content-type']).toMatch(/html/);
51+
expect(result.payload).toContain('Static website');
52+
});
53+
});
54+
});
55+
56+
describe('GET /index.html', () => {
57+
it('should return index page', async () => {
58+
return app
59+
.inject({
60+
method: 'GET',
61+
url: '/index.html'
62+
})
63+
.then((result) => {
64+
expect(result.statusCode).toEqual(200);
65+
expect(result.payload).toContain('Static website');
66+
});
67+
});
6668
});
67-
});
6869

69-
describe('GET /logo.svg', () => {
70-
it('should return logo', async () => {
71-
return app
72-
.inject({
73-
method: 'GET',
74-
url: '/logo.svg'
75-
})
76-
.then((result) => {
77-
expect(result.statusCode).toEqual(200);
78-
expect(result.headers['content-type']).toMatch(/image/);
79-
});
70+
describe('GET /logo.svg', () => {
71+
it('should return logo', async () => {
72+
return app
73+
.inject({
74+
method: 'GET',
75+
url: '/logo.svg'
76+
})
77+
.then((result) => {
78+
expect(result.statusCode).toEqual(200);
79+
expect(result.headers['content-type']).toMatch(/image/);
80+
});
81+
});
82+
});
83+
84+
describe('when trying to get a non-existing file', () => {
85+
it('should return 404', async () => {
86+
return app
87+
.inject({
88+
method: 'GET',
89+
url: '/404'
90+
})
91+
.then((result) => {
92+
expect(result.statusCode).toEqual(404);
93+
});
94+
});
95+
});
96+
97+
afterAll(async () => {
98+
await app.close();
8099
});
81100
});
82101

83-
afterAll(async () => {
84-
await app.close();
102+
describe('when "fallthrough" option is set to "false"', () => {
103+
beforeAll(async () => {
104+
app = await NestFactory.create(
105+
AppModule.withoutFallthrough(),
106+
new FastifyAdapter(),
107+
{
108+
logger: new NoopLogger()
109+
}
110+
);
111+
app.setGlobalPrefix('api');
112+
113+
await app.init();
114+
await app.getHttpAdapter().getInstance().ready();
115+
});
116+
117+
describe('GET /api', () => {
118+
it('should return "Hello, world!"', async () => {
119+
return app
120+
.inject({
121+
method: 'GET',
122+
url: '/api'
123+
})
124+
.then((result) => {
125+
expect(result.statusCode).toEqual(200);
126+
expect(result.payload).toEqual('Hello, world!');
127+
});
128+
});
129+
});
130+
131+
describe('GET /', () => {
132+
it('should return HTML file', async () => {
133+
return app
134+
.inject({
135+
method: 'GET',
136+
url: '/'
137+
})
138+
.then((result) => {
139+
expect(result.statusCode).toEqual(200);
140+
expect(result.headers['content-type']).toMatch(/html/);
141+
expect(result.payload).toContain('Static website');
142+
});
143+
});
144+
});
145+
146+
describe('GET /index.html', () => {
147+
it('should return index page', async () => {
148+
return app
149+
.inject({
150+
method: 'GET',
151+
url: '/index.html'
152+
})
153+
.then((result) => {
154+
expect(result.statusCode).toEqual(200);
155+
expect(result.payload).toContain('Static website');
156+
});
157+
});
158+
});
159+
160+
describe('GET /logo.svg', () => {
161+
it('should return logo', async () => {
162+
return app
163+
.inject({
164+
method: 'GET',
165+
url: '/logo.svg'
166+
})
167+
.then((result) => {
168+
expect(result.statusCode).toEqual(200);
169+
expect(result.headers['content-type']).toMatch(/image/);
170+
});
171+
});
172+
});
173+
174+
describe('when trying to get a non-existing file', () => {
175+
it('should returns index page', async () => {
176+
return app
177+
.inject({
178+
method: 'GET',
179+
url: '/404'
180+
})
181+
.then((result) => {
182+
expect(result.statusCode).toEqual(200);
183+
expect(result.payload).toContain('Static website');
184+
});
185+
});
186+
});
187+
188+
afterAll(async () => {
189+
await app.close();
190+
});
85191
});
86192
});

0 commit comments

Comments
 (0)