Summary
The OpenAPI generator (src/openapi.ts) produces an inaccurate document for any router with more than one method on a path, or with non-GET/POST routes. Operations silently disappear.
Reproduction
import { createEndpoint, createRouter } from "better-call";
import { z } from "zod";
const createTicket = createEndpoint("/tickets",
{ method: "POST", body: z.object({ subject: z.string() }) }, async () => ({}));
const listTickets = createEndpoint("/tickets",
{ method: "GET" }, async () => ({}));
const updateTicket = createEndpoint("/tickets/:id",
{ method: "PATCH", body: z.object({ status: z.string() }) }, async () => ({}));
const router = createRouter({ createTicket, listTickets, updateTicket });
// GET the reference route and inspect paths…
Expected
/tickets -> get, post
/tickets/{id} -> patch
Actual
/tickets -> get # POST clobbered
/tickets/:id # PATCH dropped entirely; ":id" never templated
Root causes
- Per-path overwrite.
paths[value.path] = { get: … } / = { post: … } reassigns the whole path item, so whichever endpoint is processed last wins. A POST registered before a GET on the same path is lost.
- Only GET and POST are handled. There is no branch for PATCH/PUT/DELETE, so those operations never appear.
:id is never templated and no path parameters are emitted.
- Scalar request-body fields render empty —
getRequestBody only recurses into ZodObject properties, so string/number fields yield properties: {}.
- Module-level
paths is shared across generator() calls, leaking state between documents.
- Hardcoded auth. Every operation asserts
bearerAuth and the document asserts a top-level apiKeyCookie, neither of which is defined in components.securitySchemes — and neither is correct for a service that authenticates differently.
getHTML emits invalid JS (unquoted theme/title/description interpolation into the Scalar config).
Environment
better-call 2.0.5.
Fix
PR #153 rewrites the generator to merge methods per path, emit all documented verbs, template path params, extract bodies/params via the library-agnostic ~standard.jsonSchema interface, and make security document-driven (auth-agnostic by default).
🤖 Generated with Claude Code
Summary
The OpenAPI generator (
src/openapi.ts) produces an inaccurate document for any router with more than one method on a path, or with non-GET/POST routes. Operations silently disappear.Reproduction
Expected
Actual
Root causes
paths[value.path] = { get: … }/= { post: … }reassigns the whole path item, so whichever endpoint is processed last wins. APOSTregistered before aGETon the same path is lost.:idis never templated and no path parameters are emitted.getRequestBodyonly recurses intoZodObjectproperties, so string/number fields yieldproperties: {}.pathsis shared acrossgenerator()calls, leaking state between documents.bearerAuthand the document asserts a top-levelapiKeyCookie, neither of which is defined incomponents.securitySchemes— and neither is correct for a service that authenticates differently.getHTMLemits invalid JS (unquotedtheme/title/descriptioninterpolation into the Scalar config).Environment
better-call 2.0.5.
Fix
PR #153 rewrites the generator to merge methods per path, emit all documented verbs, template path params, extract bodies/params via the library-agnostic
~standard.jsonSchemainterface, and make security document-driven (auth-agnostic by default).🤖 Generated with Claude Code