Skip to content

feat(plugin-axios,plugin-fetch): add unwrap() to every generated call - #820

Open
stijnvanhulle wants to merge 3 commits into
mainfrom
claude/client-unwrap-method
Open

feat(plugin-axios,plugin-fetch): add unwrap() to every generated call#820
stijnvanhulle wants to merge 3 commits into
mainfrom
claude/client-unwrap-method

Conversation

@stijnvanhulle

@stijnvanhulle stijnvanhulle commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

🎯 Changes

An alternative to the returnType option in #818, requested in kubb-labs/discussions#3952: a way to get a generated call's success body without destructuring { data } at every call site.

This takes the Redux Toolkit approach instead of a plugin-wide default. Every generated call (standalone function or SDK method) now resolves to a promise with an extra unwrap() method. unwrap() resolves to the bare success body, or rejects with error for a result that carried one (only reachable with throwOnError: false, since the throwing path never resolves an error).

const pet = await getPetById({ path: { petId: 1 } }).unwrap()

await getPetById(...) still resolves to the full { status, data, error, contentType, request, response } result, exactly as before. There's no option to turn this on or off, it's just there. That also means it composes cleanly with the query plugins: @kubb/plugin-react-query, @kubb/plugin-vue-query, @kubb/plugin-swr, and @kubb/plugin-mcp all call the generated client directly and await it, so their generated hooks are byte-for-byte unchanged.

Built independently of #818's returnType option (this branch is off main, not stacked on it), so the two designs can be reviewed and land separately.

How it works

  • plugin-axios/plugin-fetch runtime templates (templates/*.ts) add UnwrapResult, Unwrappable, and a withUnwrap() helper that attaches .unwrap() to a call's result promise.
  • internals/client: buildResultType now always wraps RequestResult in Unwrappable, and buildReturnStatement wraps every call in withUnwrap(...). Both the standalone functions and the class-based SDK pick this up automatically.
  • Every tests/3.0.x e2e snapshot and package-level generator snapshot that embeds a generated operation or .kubb/client.ts was regenerated. I checked the diff by hand: it's mechanical.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is for the docs (no release).

🤖 Generated with Claude Code

https://claude.ai/code/session_012DSVW79h2FdFDkoWWr6PwN

kubb-labs/discussions#3952 asked for a way to get a generated call's
success body without destructuring { data } at every call site.
Rather than a plugin-wide returnType default (see PR #818), take the
Redux-Toolkit approach: every call's promise gains an unwrap() method,
resolving to the bare success body or rejecting with `error`. Plain
`await getPetById(...)` keeps returning the full result, so existing
code and the query plugins (react-query, vue-query, swr, mcp) are
unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012DSVW79h2FdFDkoWWr6PwN
@codesandbox

codesandbox Bot commented Sep 1, 2026

Copy link
Copy Markdown

Review or Edit in CodeSandbox

Open the branch in Web EditorVS CodeInsiders

Open Preview

@changeset-bot

changeset-bot Bot commented Sep 1, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a79631d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@kubb/plugin-axios Minor
@kubb/plugin-fetch Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Sep 1, 2026

Copy link
Copy Markdown
More templates

@kubb/plugin-axios

npm i https://pkg.pr.new/@kubb/plugin-axios@820

@kubb/plugin-cypress

npm i https://pkg.pr.new/@kubb/plugin-cypress@820

@kubb/plugin-faker

npm i https://pkg.pr.new/@kubb/plugin-faker@820

@kubb/plugin-fetch

npm i https://pkg.pr.new/@kubb/plugin-fetch@820

@kubb/plugin-mcp

npm i https://pkg.pr.new/@kubb/plugin-mcp@820

@kubb/plugin-msw

npm i https://pkg.pr.new/@kubb/plugin-msw@820

@kubb/plugin-react-query

npm i https://pkg.pr.new/@kubb/plugin-react-query@820

@kubb/plugin-redoc

npm i https://pkg.pr.new/@kubb/plugin-redoc@820

@kubb/plugin-swr

npm i https://pkg.pr.new/@kubb/plugin-swr@820

@kubb/plugin-ts

npm i https://pkg.pr.new/@kubb/plugin-ts@820

@kubb/plugin-vue-query

npm i https://pkg.pr.new/@kubb/plugin-vue-query@820

@kubb/plugin-zod

npm i https://pkg.pr.new/@kubb/plugin-zod@820

commit: a79631d

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Size Change: +1.22 kB (+0.39%)

Total Size: 313 kB

📦 View Changed
Filename Size Change
packages/plugin-axios/dist/index.cjs 15.6 kB +302 B (+1.97%)
packages/plugin-axios/dist/index.js 15.1 kB +309 B (+2.09%)
packages/plugin-fetch/dist/index.cjs 15.6 kB +301 B (+1.97%)
packages/plugin-fetch/dist/index.js 15.1 kB +309 B (+2.09%)
ℹ️ View Unchanged
Filename Size
packages/plugin-axios/dist/rolldown-runtime-C0LytTxp.js 168 B
packages/plugin-cypress/dist/index.cjs 5.71 kB
packages/plugin-cypress/dist/index.js 5.62 kB
packages/plugin-cypress/dist/rolldown-runtime-C0LytTxp.js 168 B
packages/plugin-faker/dist/index.cjs 14.5 kB
packages/plugin-faker/dist/index.js 14.4 kB
packages/plugin-faker/dist/rolldown-runtime-C0LytTxp.js 168 B
packages/plugin-fetch/dist/rolldown-runtime-C0LytTxp.js 168 B
packages/plugin-mcp/dist/index.cjs 10 kB
packages/plugin-mcp/dist/index.js 9.54 kB
packages/plugin-mcp/dist/rolldown-runtime-C0LytTxp.js 168 B
packages/plugin-msw/dist/index.cjs 5.81 kB
packages/plugin-msw/dist/index.js 5.72 kB
packages/plugin-msw/dist/rolldown-runtime-C0LytTxp.js 168 B
packages/plugin-react-query/dist/index.cjs 17.8 kB
packages/plugin-react-query/dist/index.js 17.2 kB
packages/plugin-react-query/dist/rolldown-runtime-C0LytTxp.js 168 B
packages/plugin-redoc/dist/index.cjs 2.21 kB
packages/plugin-redoc/dist/index.js 1.74 kB
packages/plugin-redoc/dist/rolldown-runtime-C0LytTxp.js 168 B
packages/plugin-swr/dist/index.cjs 12 kB
packages/plugin-swr/dist/index.js 11.4 kB
packages/plugin-swr/dist/rolldown-runtime-C0LytTxp.js 168 B
packages/plugin-ts/dist/index.cjs 23.8 kB
packages/plugin-ts/dist/index.js 23.1 kB
packages/plugin-ts/dist/rolldown-runtime-CNktS9qV.js 291 B
packages/plugin-vue-query/dist/index.cjs 14.8 kB
packages/plugin-vue-query/dist/index.js 14.3 kB
packages/plugin-vue-query/dist/rolldown-runtime-C0LytTxp.js 168 B
packages/plugin-zod/dist/index.cjs 19.7 kB
packages/plugin-zod/dist/index.js 19.6 kB
packages/plugin-zod/dist/rolldown-runtime-C0LytTxp.js 168 B

compressed-size-action

… in withUnwrap

CI caught this on this repo's own examples/axios: an operation with no
documented 2xx response (deletePet, 400 only) failed to typecheck.
withUnwrap(request(callConfig)) inferred its generic from the client's
own CallResult type, so the outer `as Unwrappable<RequestResult<...>>`
cast carried two incompatible `.then` overloads and TypeScript rejected
it as too narrow a conversion.

Casting the call to RequestResult first, then wrapping, keeps
withUnwrap's generic inferred as RequestResult directly, matching what
the code did before this feature and avoiding the second cast entirely.
Reproduced the exact failure in isolation, confirmed the fix, then
verified end-to-end with a real `pnpm generate` against a built
plugin-axios (this sandbox has no prebuilt dist, so pnpm generate
otherwise fails to resolve the plugin).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012DSVW79h2FdFDkoWWr6PwN
@dosubot

dosubot Bot commented Sep 1, 2026

Copy link
Copy Markdown

📄 Knowledge review

🆕 New pages

1 new page was drafted from this PR.

Page Library
Using Generated Client Functions - Response Handling and unwrap() Kubb's Space

Leave Feedback Ask Dosu about plugins Add Dosu to your team

stijnvanhulle pushed a commit that referenced this pull request Sep 1, 2026
…a shared runtime helper

/simplify's reuse, simplification, and altitude passes all converged
on the same finding: the generated return statement inlined
`config.throwOnError ?? true ? result.data : result` as a literal
string, restating a default the runtime already applies internally,
once per operation across every spec. The sibling `text/event-stream`
path already centralizes its post-processing in a runtime function
(toEventStream); this does the same for returnType: 'data' with a new
unwrapResult(promise, throwOnError) exported from both templates.

Verified with a real `pnpm generate` against a built plugin-axios,
covering the no-2xx-response edge case that broke CI on PR #820.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012DSVW79h2FdFDkoWWr6PwN
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants