Summary
GET /api/openapi serves a 1.1 MB static generated document (public/openapi.generated.json), but the handler deep-walks, deep-copies, and re-serializes the entire structure on every request, and sends no cache headers. On Workers that is tens of milliseconds of CPU per hit for zero behavioral gain.
Evidence
src/lib/api/routes/openapi.ts:4-9 — getOpenApiRoute returns jsonResponse(openApiSpec).
src/lib/api/responses.ts:17-27 — jsonResponse runs JSON.stringify(serializeDatesDeep(body)).
src/lib/time/serialize-date-output.ts:11-25 — serializeDatesDeep recursively walks every object/array node to convert Date instances. The OpenAPI JSON is a static import containing no Date values, so the walk allocates a full deep copy of ~1.1 MB of nested objects only to stringify it — per request.
- No
Cache-Control/ETag is set on the response (nothing in openapi.ts, and the global hook only adds cache headers to HTML responses, src/hooks.server.ts:226-237).
- The document only changes at build time, so it is trivially cacheable both in-isolate and at the CDN edge.
Suggested fix
Pre-serialize once at module scope and serve the cached string (or stream the imported asset directly), bypassing serializeDatesDeep, e.g.:
const body = JSON.stringify(openApiSpec);
return new Response(body, {
headers: {
"content-type": "application/json; charset=utf-8",
"cache-control": "public, max-age=300",
},
});
Adding a short max-age (or an ETag) also lets Cloudflare cache it at the edge for doc consumers.
Summary
GET /api/openapiserves a 1.1 MB static generated document (public/openapi.generated.json), but the handler deep-walks, deep-copies, and re-serializes the entire structure on every request, and sends no cache headers. On Workers that is tens of milliseconds of CPU per hit for zero behavioral gain.Evidence
src/lib/api/routes/openapi.ts:4-9—getOpenApiRoutereturnsjsonResponse(openApiSpec).src/lib/api/responses.ts:17-27—jsonResponserunsJSON.stringify(serializeDatesDeep(body)).src/lib/time/serialize-date-output.ts:11-25—serializeDatesDeeprecursively walks every object/array node to convertDateinstances. The OpenAPI JSON is a static import containing noDatevalues, so the walk allocates a full deep copy of ~1.1 MB of nested objects only to stringify it — per request.Cache-Control/ETagis set on the response (nothing inopenapi.ts, and the global hook only adds cache headers to HTML responses,src/hooks.server.ts:226-237).Suggested fix
Pre-serialize once at module scope and serve the cached string (or stream the imported asset directly), bypassing
serializeDatesDeep, e.g.:Adding a short
max-age(or an ETag) also lets Cloudflare cache it at the edge for doc consumers.