Skip to content

[api] GET /api/openapi deep-copies and re-serializes a 1.1MB static spec on every request #489

Description

@tiankaima

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-9getOpenApiRoute returns jsonResponse(openApiSpec).
  • src/lib/api/responses.ts:17-27jsonResponse runs JSON.stringify(serializeDatesDeep(body)).
  • src/lib/time/serialize-date-output.ts:11-25serializeDatesDeep 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: apiREST, MCP, OpenAPI, webhooks, iCalendar, and externally consumed contractsmaintenanceInternal refactoring, tooling, tests, CI, dependencies, or operational upkeepreleased

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions