Skip to content

feat(openapi): interface output + fetch-client profiles#422

Open
ALagoni97 wants to merge 1 commit into
mainfrom
feat/openapi-interface-profiles
Open

feat(openapi): interface output + fetch-client profiles#422
ALagoni97 wants to merge 1 commit into
mainfrom
feat/openapi-interface-profiles

Conversation

@ALagoni97

Copy link
Copy Markdown
Contributor

What & why

The CLI is built on Modelina, which emits class-based models with marshal()/unmarshal()/validate() — idiomatic for the AsyncAPI/broker world, but the wrong shape for OpenAPI REST consumers, who expect plain interface types + a thin fetch client (the bar set by openapi-typescript, orval, kubb, heyapi). Today a consumer writes new GetV2TransactionsParameters({...}) and resp.data.items[0].marshal() — non-idiomatic ceremony that makes the tool hard to pick for REST.

This PR lets OpenAPI generate pure interfaces + an optional fetch client, exposed through two profiles (types, client), without forking the engine and without changing AsyncAPI or any broker-protocol output. Class mode stays the default everywhere (fully backward compatible).

How it works

export default {
  inputType: 'openapi',
  inputPath: './openapi.json',
  language: 'typescript',
  profile: 'client', // or 'types'
  generators: []      // explicit generators are appended as overrides
};
  • types → interface payloads + interface parameters (with standalone serializers) + headers. No channels/client.
  • client → everything in types plus the HTTP fetch client.

Engine

  • payloads: new modelType: 'class' | 'interface' (default class). Interface mode drops the TS_COMMON_PRESET { marshalling } + validation presets and sets Modelina modelType: 'interface'.
  • parameters: same modelType. Interface mode emits a plain interface + free functions serialize<Name>QueryParameters / serialize<Name>PathParameters / serialize<Name>Url (replacing the class getChannelWithParameters), reusing the exact style/explode logic from the class path. Surfaced via a new parameterFunctions map (mirroring headerFunctions).

HTTP client (interface mode)

JSON.stringify(payload) body, response cast to the interface, URL via serialize<Name>Url(...), plain-interface parameters field, interface + serializer imports. Broker protocols are untouched.

Profiles

profile: 'types' | 'client' on the OpenAPI config, expanded in realizeConfiguration() before validation. Distinct from the existing types preset (simple type aliases) — documented in docs/generators/profiles.md.

Testing

  • New unit tests: interface-mode parameter rendering (with snapshot) + both profile expansions.
  • Regenerated schemas/*.json + docs from Zod; added docs/generators/profiles.md.
  • npm run build, npm run lint (max-warnings 0), npm test (657 tests) all green.
  • ✅ End-to-end codegen generate for both profiles against the runtime OpenAPI spec — output type-checks under tsc --strict.

Not included (with reasons)

  • Blackbox fixture: the blackbox harness only loads asyncapi/jsonschema documents (no OpenAPI branch) and runs every config × every schema — an OpenAPI config there would break the suite. Validated the same thing directly with tsc --strict on generated output instead.
  • Runtime suite / playground iterate: require Docker brokers / the platform checkout. The runtime codegen-openapi.mjs still uses class mode (unchanged, so broker regression gates pass); promoting hand-written interface output to a runtime check is the natural follow-up.

Note for reviewers

Profiles currently prepend their generators and treat explicitly-listed generators as appended overrides. If you'd prefer profiles be all-or-nothing (reject stray generators), that's a small change.

🤖 Generated with Claude Code

Let OpenAPI generate plain `interface` types and an optional fetch client
through two high-level profiles (`types`, `client`), the idiomatic shape for
REST consumers — without forking Modelina and without changing AsyncAPI or any
broker-protocol output (class mode stays the default everywhere).

Engine:
- payloads: `modelType: 'class' | 'interface'` (default class). Interface mode
  drops the marshalling + validation presets and sets Modelina interface mode.
- parameters: same `modelType`. Interface mode emits a plain interface plus
  standalone serializers (serialize<Name>QueryParameters/PathParameters/Url),
  reusing the exact style/explode logic from the class path. Exposed via a new
  `parameterFunctions` map on the render type.

HTTP client (interface mode): JSON.stringify body, response cast to the
interface, URL via serialize<Name>Url, plain-interface `parameters` field, and
interface + serializer imports. Broker protocols untouched.

Profiles: `profile: 'types' | 'client'` on the OpenAPI config, expanded in
realizeConfiguration() before validation. Distinct from the `types` preset.

Tests/docs/assets: interface-mode + profile-expansion unit tests, regenerated
schemas + docs, new docs/generators/profiles.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ALagoni97
ALagoni97 requested a review from jonaslagoni as a code owner July 16, 2026 15:25
@vercel

vercel Bot commented Jul 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
the-codegen-project Ready Ready Preview, Comment Jul 16, 2026 3:27pm
the-codegen-project-mcp Ready Ready Preview, Comment Jul 16, 2026 3:27pm

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.

1 participant