fix: handle no-body / optional-body endpoints and empty 2xx responses#5
Merged
Conversation
…body
hasBody was assumed for every POST/PATCH/PUT, so the generated command always
emitted a body field (`body: never` for no-body endpoints), forcing callers into
`as any`. Now detect the actual requestBody per operation (resolving operations[]
refs, incl. quoted operationIds like "activity/star-repo") and emit:
- path + body -> z.object({ path, body })
- path, no body -> z.object({ path })
- no path, body -> body-directly
- no path/body -> z.void() (no-arg action)
Runtime: a path-only input no longer forwards { path } as the request body.
Adds Stripe + GitHub api.d.ts fixtures (real specs, ~1800 endpoints) which caught
the quoted-operationId gap. 107 tests pass. No version bump (CI release handles it).
…l bodies Gotchas found while validating the no-body fix against real specs: - handleResponse treated an empty 2xx body (204 No Content / void endpoints) as a 404; now returns success. Also uses response.status for errors instead of a Nest-specific error.statusCode, so any API's status codes surface. - GetRequestBody only matched a required `requestBody:`, typing every optional-body endpoint (`requestBody?:`) as `never` — 490 such ops in the Stripe fixture, 66 in GitHub. Now matches both. Validated required+optional resolve via tsc. 110 tests pass. No version bump.
This was referenced Jul 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #3 (the PATCH/PUT bare-body fix). While wiring the generated client into a real app, several related gotchas surfaced around endpoints that have no request body or an optional one, plus empty successful responses. All are fixed here with tests, including large real-world fixtures.
Fixes
1. Generator: stop requiring a body for no-body endpoints
hasBodywas assumed for every POST/PATCH/PUT, so the command schema always emitted abodyfield. For an endpoint with no request body that'sbody: never, forcing callers intoas any. Now the generator detects the actualrequestBodyper operation and emits:z.object({ path, body })z.object({ path })z.void()(no-argument action command)Detection resolves
operations["Name"]refs against theoperationsinterface, including quoted operationIds like"activity/star-repo-for-authenticated-user"(GitHub/Stripe style).2. Runtime: don't forward
{ path }as the request bodybuildRequestOptionsnow resolves the body ashasBody ? input.body : hasPath ? undefined : input, so a path-only input sends params with no body (previously it would have sent{ path }as the body).3. Runtime: empty 2xx responses are success, not 404
handleResponsetreated any response with nodataas a 404 — breaking 204 No Content and void/action endpoints. It now returns success on an ok response with an empty body, and usesresponse.status(works for any API) instead of a Nest-specificerror.statusCode.4. Types:
GetRequestBodymatches optional bodiesIt only matched a required
requestBody:, so every optional-body operation (requestBody?:) typed tonever— 490 such ops in the Stripe fixture, 66 in GitHub. Now matches both required and optional. Verified required + optional resolve correctly viatsc.Tests
test/fixtures/{stripe,github}.d.ts(api.d.ts from the public Stripe & GitHub OpenAPI specs, ~1,800 endpoints). These caught the quoted-operationId gap and guard against regressions at scale.src/: no findings.No version bump — the release workflow handles it.
Note (not fixed here)
The
expectTypeOftype tests aren't currently type-checked byvitest run(notypecheckconfig;tsconfigexcludestest/). Worth enablingvitest --typecheckin a follow-up so the type assertions actually run in CI.