feat: support multiple content types in requestBody#91
Conversation
When an OpenAPI operation declares more than one content type for its requestBody (e.g. application/json + multipart/form-data), plugins now generate proper support for all of them instead of silently using only the first entry. Changes: - plugin-ts/typeGenerator: emits individual types per content type (e.g. UploadFileJsonData, UploadFileFormData) plus a union alias (UploadFileData = UploadFileJsonData | UploadFileFormData) - plugin-client/Client.tsx: adds a contentType parameter with literal union type and default; uses runtime ternary for form-data dispatch - plugin-client generators: fix isFormData/hasFormData checks to scan all content entries via .some() instead of only content[0] - internals/tanstack-query: extend buildMutationArgParams to accept optional extraBodyParams for contentType forwarding - plugin-react-query/MutationOptions + Mutation: propagate contentType through mutation variables and client call when multiple content types - plugin-react-query/mutationGenerator: make buildFormData import conditional on operation actually using multipart/form-data - Single-content-type operations are unchanged (backwards-compatible) Tests added for multiContentType case in all three generators. https://claude.ai/code/session_011Q7vkeTEWgmr3TKnfGue4q
Review or Edit in CodeSandboxOpen the branch in Web Editor • VS Code • Insiders |
🦋 Changeset detectedLatest commit: a35c1b2 The changes in this PR will be included in the next version bump. This PR includes changesets to release 13 packages
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 |
|
Auto fix applied for linting and formating |
More templates
@kubb/plugin-client
@kubb/plugin-cypress
@kubb/plugin-faker
@kubb/plugin-mcp
@kubb/plugin-msw
@kubb/plugin-react-query
@kubb/plugin-redoc
@kubb/plugin-ts
@kubb/plugin-vue-query
@kubb/plugin-zod
commit: |
Real Petstore operations (AddPet, UpdatePet, PlaceOrder, etc.) declare multiple content types, so snapshots now reflect the new per-content-type type aliases and union alias generated by plugin-ts, plus the contentType parameter added to client functions by plugin-client. https://claude.ai/code/session_011Q7vkeTEWgmr3TKnfGue4q
Content types like 'application/json-patch+json; x-api-version=1.0' were producing invalid TypeScript identifiers (e.g. 'Json-patch+json; x-api-version=1.0Data') because getContentTypeSuffix did not strip content-type parameters or sanitize non-alphanumeric characters. Fix: strip the '; param=value' portion first, then split the subtype on any non-alphanumeric run and join the resulting words in PascalCase. Also add deduplication with a numeric suffix for the rare case where multiple content types collapse to the same identifier (e.g. 'text/json' and 'application/*+json' both resolve to 'Json'). Fixes E2E failure on the requestBody.yaml schema (GitHub issue #80). https://claude.ai/code/session_011Q7vkeTEWgmr3TKnfGue4q
Petstore operations like AddPet, UpdatePet, PlaceOrder, CreateUser, and UpdateUser declare multiple content types (application/json, application/xml, application/x-www-form-urlencoded), so the generated examples now include per-content-type type aliases, union aliases, and contentType parameters. https://claude.ai/code/session_011Q7vkeTEWgmr3TKnfGue4q
…a upload example
- plugin-vue-query/Mutation.tsx: add buildContentTypeParam helper and
thread contentType through buildMutationArgParams (for the mutation
variables type) and clientCallParamsNode extraParams (for the actual
client call), matching the react-query fix already in place.
- examples/*/petStore.yaml: extend the uploadFile operation with an
application/json content type (upload-by-URL with {url: string}) in
addition to the existing multipart/form-data ({file: binary}). This
gives every example a real operation with two different schemas per
content type, demonstrating the full feature end-to-end.
- Regenerated all affected examples and updated integration snapshots.
https://claude.ai/code/session_011Q7vkeTEWgmr3TKnfGue4q
- Extract getContentTypeInfo and buildContentTypeParams into @internals/tanstack-query and re-export from react-query/vue-query utils - Add getContentTypeInfo to plugin-client utils for local use - Remove duplicated inline content-type computation from MutationOptions, vue-query/Mutation, and Client components - Move getContentTypeSuffix and getPerContentTypeName to module level in typeGenerator - Convert requestType and responseType IIFEs to named inner functions https://claude.ai/code/session_011Q7vkeTEWgmr3TKnfGue4q
|
@copilot merge main and update test snapshots |
…type-request-kmujk # Conflicts: # packages/plugin-client/src/utils.ts Co-authored-by: stijnvanhulle <5904681+stijnvanhulle@users.noreply.github.com>
Agent-Logs-Url: https://github.com/kubb-labs/plugins/sessions/c85ca4a2-d441-4b4d-bf3b-b7e89e6f475a Co-authored-by: stijnvanhulle <5904681+stijnvanhulle@users.noreply.github.com>
|
@copilot contentType should be part of the config, update also axios and fetch clients |
Agent-Logs-Url: https://github.com/kubb-labs/plugins/sessions/c8508a2d-4703-4866-b618-7610857dba6c Co-authored-by: stijnvanhulle <5904681+stijnvanhulle@users.noreply.github.com>
Done. Changes:
Commit: de445bc |
|
@copilot resolve examples typecheck and generation |
Fixed. The root cause was that Changes:
Commit: 3bff86a Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
|
@copilot merge main and resolve merge conflicts |
…type-request-kmujk Co-authored-by: stijnvanhulle <5904681+stijnvanhulle@users.noreply.github.com>
Agent-Logs-Url: https://github.com/kubb-labs/plugins/sessions/3a26c654-ac62-448a-aa8c-78dafa9bb8d1 Co-authored-by: stijnvanhulle <5904681+stijnvanhulle@users.noreply.github.com>
Done. Merged main (beta.4 release + Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
|
@copilot research if this appraoch make sense and add plan in what we have and what is still needed |
Agent-Logs-Url: https://github.com/kubb-labs/plugins/sessions/e4abfa59-b12a-410e-a773-f902bd2bef3f Co-authored-by: stijnvanhulle <5904681+stijnvanhulle@users.noreply.github.com>
Done in 22e0d48. I reviewed the approach with a gpt-5.5 research pass, updated |
Agent-Logs-Url: https://github.com/kubb-labs/plugins/sessions/326bcae9-a45c-440a-a6c1-0130f8b516f8 Co-authored-by: stijnvanhulle <5904681+stijnvanhulle@users.noreply.github.com>
|
@copilot resolve merge conflicts |
…type-request-kmujk Co-authored-by: stijnvanhulle <5904681+stijnvanhulle@users.noreply.github.com>
Merged latest |
| with: | ||
| node-version: '22' | ||
|
|
||
| - name: Install RTK |
There was a problem hiding this comment.
@copilot not needed anymore, our setup config has rtk already
Summary
Implements issue #80 and the plan in
docs/plans/multi-content-type-request-body.md.When an OpenAPI operation declares multiple content types for its
requestBody(e.g.application/jsonandmultipart/form-data), all three plugins now generate proper support for every content type instead of silently ignoring all but the first.UploadFileJsonData,UploadFileFormData) followed by a union alias (type UploadFileData = UploadFileJsonData | UploadFileFormData). Single content type output is unchanged.contentTypeparameter (literal union + default from first declared type) and uses a runtime ternary to dispatch between form-data and JSON paths.hasFormDatanow checks all content entries via.some()instead of onlycontent[0].contentTypeis forwarded through mutation variables into the client call;buildFormDataimport is now conditional on the operation actually usingmultipart/form-data.buildMutationArgParamsaccepts an optionalextraBodyParamsarray so callers can inject additional body-level params (used forcontentType).All single-content-type operations are backwards-compatible — generated output is identical to before.
TODO
contentTypeto config