Skip to content

Commit 5c38bd3

Browse files
committed
feature: Performance improvements
Lazy-load all page components in App.tsx (565KB main chunk -> 200KB, per-page chunks fetched on nav). Register @fastify/compress globally on the BFF (gzip/brotli, SSE exempt via reply.hijack()). Cache hashed /assets/* for 1y immutable; index.html stays no-cache. Targets FCP regression vs the HTMX app (1.4s vs 0.6s). Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
1 parent 583da78 commit 5c38bd3

6 files changed

Lines changed: 294 additions & 53 deletions

File tree

server/package-lock.json

Lines changed: 184 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616
"lint": "tsc -p tsconfig.json --noEmit"
1717
},
1818
"dependencies": {
19+
"@fastify/compress": "^9.2.0",
1920
"@fastify/cookie": "^11.1.2",
20-
"@fastify/static": "^10.1.2",
2121
"@fastify/csrf-protection": "^8.0.1",
2222
"@fastify/redis": "^8.0.0",
2323
"@fastify/reply-from": "^12.6.4",
24+
"@fastify/static": "^10.1.2",
2425
"fastify": "^5.11.2",
2526
"fastify-plugin": "^6.0.0",
2627
"ioredis": "^5.11.1",

server/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import Fastify from "fastify";
1111

1212
import { config } from "./config.js";
13+
import compressPlugin from "./plugins/compress.js";
1314
import cookiePlugin from "./plugins/cookie.js";
1415
import csrfPlugin from "./plugins/csrf.js";
1516
import redisPlugin from "./plugins/redis.js";
@@ -30,6 +31,7 @@ await fastify.register(cookiePlugin);
3031
await fastify.register(redisPlugin);
3132
await fastify.register(sessionPlugin);
3233
await fastify.register(csrfPlugin);
34+
await fastify.register(compressPlugin);
3335
await fastify.register(staticPlugin);
3436

3537
fastify.get("/healthz", async () => ({ ok: true }));

server/src/plugins/compress.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Location: ./client/server/src/plugins/compress.ts
2+
// Copyright contributors to the MCP-CONTEXT-FORGE project
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// Global response compression (brotli > gzip > deflate, by Accept-Encoding).
6+
// Must be registered before staticPlugin — @fastify/compress's global hook
7+
// only wraps replies from routes registered after it. SSE routes are exempt
8+
// automatically: proxy-sse.ts calls reply.hijack(), which skips the onSend
9+
// chain this plugin hooks into.
10+
11+
import fastifyCompress from "@fastify/compress";
12+
import type { FastifyInstance } from "fastify";
13+
import fp from "fastify-plugin";
14+
15+
export default fp(
16+
async function compressPlugin(fastify: FastifyInstance) {
17+
await fastify.register(fastifyCompress, { global: true });
18+
},
19+
{ name: "compressPlugin" },
20+
);

server/src/plugins/static.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ export default fp(
3232
root: PUBLIC_DIR,
3333
prefix: "/",
3434
index: false, // '/' is handled explicitly by routes/app.ts, for the auth check
35+
// Vite content-hashes every /assets/* filename, so those are safe to
36+
// cache for a year. index.html itself overrides this below — it must
37+
// always revalidate, since it's what points at the current hashes.
38+
maxAge: "1y",
39+
immutable: true,
3540
});
3641

3742
fastify.setNotFoundHandler((request: FastifyRequest, reply: FastifyReply) => {
@@ -58,7 +63,7 @@ export default fp(
5863
statusCode: 404,
5964
});
6065
}
61-
return reply.sendFile("index.html");
66+
return reply.sendFile("index.html", { maxAge: 0, immutable: false });
6267
});
6368
},
6469
{ name: "staticPlugin" },

0 commit comments

Comments
 (0)