Skip to content

fix(open-api): correctness fixes for ts-client multipart/cookies/response types and composite parameter types#613

Merged
cogwirrel merged 3 commits into
mainfrom
fix/ts-client-correctness
Apr 27, 2026
Merged

fix(open-api): correctness fixes for ts-client multipart/cookies/response types and composite parameter types#613
cogwirrel merged 3 commits into
mainfrom
fix/ts-client-correctness

Conversation

@nx-plugin-for-aws

@nx-plugin-for-aws nx-plugin-for-aws commented Apr 26, 2026

Copy link
Copy Markdown
Collaborator

Reason for this change

Reviewing the generated TypeScript client against a variety of OpenAPI specs — petstore v3, an Error-schema / oneOf spec, a multipart-upload spec, an edge spec with reserved keywords and readOnly fields, a streaming spec, and finally a real FastAPI service — surfaced five correctness bugs that either produce invalid TypeScript or silently break at runtime. Each is a small, localised fix and each gets a regression test.

Description of changes

  1. type: object with no additionalProperties emitted invalid TypeScript. The template rendered [key: string]: <link.typescriptType>; unconditionally, and the empty typescriptType produced [key: string]: ; — a tsc syntax error. Fall back to unknown so the emitted code stays valid.

  2. multipart/form-data bodies were sent with a boundary-less Content-Type. The generator pre-set Content-Type: multipart/form-data before fetch() had a chance to compute the boundary from the FormData body; servers cannot parse the resulting request because RFC 7578 requires the boundary= parameter. Skip the assignment for multipart bodies so the runtime populates a correct header.

  3. Cookie parameters were silently dropped. in: cookie parameters were typed on the request input but never serialised onto the wire — the user thought they were sending a session cookie and the server never saw it. Serialise them into a percent-encoded Cookie: header (both node-side and cross-origin browser callers need this explicitly).

  4. Multi-media-type request bodies produced a comma-joined Content-Type. Content-Type: 'application/json,application/xml,application/x-www-form-urlencoded' is wire-incorrect — servers parse the header as a single value. Pick one wire type (preferring application/json / +json variants, falling back to the first entry).

  5. Composite parameter schemas were never hoisted. FastAPI / Pydantic v2 encode Optional[T] parameters inline as {anyOf: [T, {type: 'null'}], title: '<name>'}. The existing hoisting pass walked request/response JSON schemas into components.schemas but didn't walk parameters[].schema, so hey-api-openapi synthesised an anonymous composite type named from the title (e.g. XClient, Session, Tag) but never emitted a declaration. Every FastAPI-generated client then failed to compile with Cannot find name 'XClient' / '"Session" is not defined'. The fix extends the hoister to walk parameter schemas too; once hoisted, the existing primitive-inlining path produces the correct string | null etc. — no lossy rewrite.

Also in the shared codegen data: normalise hey-api's in: 'formData' to in: 'body' so downstream consumers see a single body kind.

Description of how you validated changes

  • Regression tests for each scenario land alongside the existing content-type, additional-properties and fast-api spec files.
  • Full suite passes: 1731 tests, lint, build, all smoke tests (incl. Windows).
  • End-to-end sanity with tsx against a mock fetch: petstore (7 tests), complex (4 tests, including 409 error via renamed _Error), advanced (multipart → auto-boundary, Blob octet-stream, cookies, deprecated, allOf) — all green after the fix.
  • Integration tests against real servers (FastAPI, tRPC-via-trpc-to-openapi, Smithy): 29 py-client + 29 ts-client tests pass. SigV4-signed FastAPI calls through a mock API Gateway IAM proxy — 6 py + 6 ts — confirm the middleware hooks work against the IAM-auth flow.

Issue # (if applicable)

Follow-up to the review in #586.

Checklist


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

…view

- `type: object` with no `additionalProperties` (or `additionalProperties:
  true`) used to emit `[key: string]: ;` — an empty value type that fails
  `tsc` parsing.  Fall back to `unknown` when the link's typescriptType is
  empty so the generated code stays syntactically valid.
- `multipart/form-data` request bodies pre-set `Content-Type` to a bare
  `multipart/form-data` string (no `boundary=…`), which `fetch()`
  preserves verbatim — the server then fails to parse the payload because
  RFC 7578 requires the boundary parameter.  Skip the `Content-Type`
  assignment for multipart bodies so `fetch` auto-computes the boundary
  from the `FormData` value.
- Cookie parameters (`in: cookie`) were typed on the request input and
  then silently dropped at send time — the user's cookie never reached
  the server.  Serialise them into a percent-encoded `Cookie:` header
  (both node-side and cross-origin browser callers need this, since the
  browser only auto-attaches cookies on same-origin requests).
- Multi-media-type request bodies emitted `Content-Type:
  'application/json,application/xml,…'` — wire-incorrect (servers parse
  Content-Type as a single value).  Pick a single wire media type,
  preferring `application/json` / `+json` variants and falling back to
  the first entry.

Also normalises hey-api's `in: 'formData'` parameter to `in: 'body'` in
`codegen-data.ts` so language-agnostic consumers see a single body kind
(matches the py-client path).

Adds regression tests covering each scenario.
@codecov-commenter

codecov-commenter commented Apr 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.90909% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 89.10%. Comparing base (34760f8) to head (1ab5629).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
packages/nx-plugin/src/open-api/utils/normalise.ts 88.88% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #613      +/-   ##
==========================================
+ Coverage   89.07%   89.10%   +0.03%     
==========================================
  Files         107      107              
  Lines        3477     3487      +10     
  Branches      757      760       +3     
==========================================
+ Hits         3097     3107      +10     
  Misses        176      176              
  Partials      204      204              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nx-plugin-for-aws nx-plugin-for-aws force-pushed the fix/ts-client-correctness branch 2 times, most recently from 114d0e3 to 5492313 Compare April 27, 2026 21:11
FastAPI / Pydantic v2 encode `Optional[T]` parameters as inline composite
schemas on the parameter (e.g.
`{anyOf: [{type: string}, {type: null}], title: 'X-Client'}`).  The
hoisting pass walked request/response JSON schemas but not parameter
schemas, so hey-api-openapi synthesised an anonymous composite named
from the title (`XClient`, `Session`, …) without emitting a
declaration, and every generated FastAPI client failed to compile:

    types.gen.ts: Cannot find name 'XClient'
    pyright:      '"Session" is not defined'

Extends the existing hoist to also move composite parameter schemas
into `components.schemas` under `<OpId>Request<In><Name>`.
@nx-plugin-for-aws nx-plugin-for-aws force-pushed the fix/ts-client-correctness branch from 5492313 to 1ab5629 Compare April 27, 2026 23:06
@nx-plugin-for-aws nx-plugin-for-aws changed the title fix(open-api#ts-client): resolve four correctness bugs surfaced by review fix(open-api): correctness fixes for ts-client multipart/cookies/response types, plus composite parameter hoisting Apr 27, 2026
@cogwirrel cogwirrel changed the title fix(open-api): correctness fixes for ts-client multipart/cookies/response types, plus composite parameter hoisting fix(open-api): correctness fixes for ts-client multipart/cookies/response types and composite parameter types Apr 27, 2026
@cogwirrel cogwirrel merged commit e0a1fee into main Apr 27, 2026
18 checks passed
@cogwirrel cogwirrel deleted the fix/ts-client-correctness branch April 27, 2026 23:53
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.

3 participants