diff --git a/.claude/rules/protocols.md b/.claude/rules/protocols.md index 30ae9a22..d2a1917b 100644 --- a/.claude/rules/protocols.md +++ b/.claude/rules/protocols.md @@ -7,7 +7,11 @@ paths: ## Supported Protocols -NATS, Kafka, MQTT, AMQP, EventSource, HTTP Client, WebSocket +NATS, Kafka, MQTT, AMQP, EventSource, HTTP Client, HTTP Server, WebSocket + +HTTP Client and HTTP Server both live under `protocols/http/`. HTTP Server is +OpenAPI-only and generates Express handler stubs — the structural inverse of the +client. ## File Structure Per Protocol diff --git a/.claude/skills/add-protocol/SKILL.md b/.claude/skills/add-protocol/SKILL.md index 091dc900..b20588d5 100644 --- a/.claude/skills/add-protocol/SKILL.md +++ b/.claude/skills/add-protocol/SKILL.md @@ -38,3 +38,48 @@ Follow these steps to add `$ARGUMENTS` protocol support: 13. Write runtime tests with Docker containers 14. Add blackbox test configurations 15. Verify all tests pass: `npm run prepare:pr` + +## Phase 6: Downstream surfaces + +The generator is not the only place that lists protocols. The CLI wizard, the +website and the MCP server each keep their own hand-maintained copy, none of +which is generated from the Zod schema. Miss one and the protocol ships but is +unreachable from that surface. + +`test/protocol-surfaces.spec.ts` enforces this list — run it and let the failure +messages drive the work: + +```bash +npm run build && npm test -- --testPathPattern=protocol-surfaces +``` + +16. `src/commands/init.ts` — add to `AsyncAPIChannelProtocolOptions` and/or + `OpenAPIChannelProtocolOptions` depending on which inputs support it. This + also gates the `--channels-protocols` flag, so re-run + `npm run generate:commands` to refresh `docs/usage.md`. +17. `website/src/components/Playground/ConfigForm.tsx` — the `PROTOCOLS` picker. +18. `website/src/utils/configCodegen.ts` — `PROTOCOL_INPUT_COMPATIBILITY`. The + picker filters on this, so an entry missing here hides the option entirely. +19. `website/src/components/Home/Protocols/index.tsx` — the homepage marquee. +19b. `website/src/components/Home/demos.ts` — the homepage showcase. Read that + file's header comment first: every snippet must be real generator output for + the demo's *own* embedded spec, and the hand-written `index.ts` pane must + compile against it. Extract the spec, run the CLI on it, paste verbatim + (eliding long bodies with `// ...`) — never adapt a snippet from another + document or write one from memory. +20. `mcp-server/lib/data/generators.ts` — add to `protocolValues` (every MCP tool + schema derives its `z.enum` from it), plus `protocolDescriptions` and + `clientImports` in `lib/tools/integration-tools.ts` — both are + `Record`, so `tsc` will name them if you forget. Add a usage + example to `lib/data/examples.ts`. +21. Docs: a `docs/protocols/.md` page, plus the protocol lists in + `README.md`, `docs/README.md`, `docs/getting-started/protocols.md`, + `docs/generators/channels.md` and `docs/generators/README.md`. Follow the + `write-docs` skill — the page is for a reader with a task, not a summary of + the PR that built it. +22. An `examples/` project, per "no feature without docs + an example". + +Do **not** hand-edit `website/src/schemas/configuration-schema.json`, +`website/static/codegen.browser.mjs` or `mcp-server/lib/resources/bundled-docs.ts`. +They are generated copies, refreshed by `generate:playground` / +`generate:mcp:docs` in the release pipeline rather than in feature PRs. diff --git a/.claude/skills/write-docs/SKILL.md b/.claude/skills/write-docs/SKILL.md new file mode 100644 index 00000000..5d133a90 --- /dev/null +++ b/.claude/skills/write-docs/SKILL.md @@ -0,0 +1,92 @@ +--- +name: write-docs +description: Write or review user-facing documentation in docs/ so it reads for the user rather than as a record of the work that produced it +--- + +# Write user-facing documentation + +Use this whenever you add or edit anything under `docs/`, a protocol or +generator page, an `examples/*/README.md`, or the root `README.md`. + +## The failure this exists to prevent + +Docs written at the end of an implementation session tend to preserve the +*conversation* rather than serve the reader. The tell is a section that answers +a question the reader never asked, but a reviewer did: why the scope was drawn +where it was, what was considered and rejected, what the author decided not to +build. + +Real example. `docs/protocols/http_server.md` shipped with: + +```md +## Explicit non-goals + +- **No frameworks other than Express**, and no framework-agnostic core layer. +- **No listener or server construction.** +``` + +Three things are wrong with it. The support table two screens up already said +`Frameworks other than Express ❌`, so it is duplication. "No framework-agnostic +core layer" describes the shape of the *implementation*, which no user can act +on. And "non-goal" claims a permanent intent the project has not committed to — +multiple frameworks are a plausible future feature, and the docs should not +foreclose it. + +None of the other protocol pages have such a section. It existed because it was +in the PR description. + +## Capability tables answer "can I do X?" + +Every row names a feature a reader might come looking for, answered `✅` / `❌` +(with a short parenthetical when `❌` needs a pointer). See the table in +`docs/protocols/http_client.md`: `Download ❌`, `XML Based API ❌`, +`Bearer Authentication ✅`. + +Never phrase a row in negative space. `| Frameworks other than Express | ❌ |` +is not a feature anyone searches for, and it makes the reader reconstruct the +positive fact. When the answer is a choice rather than a yes/no, put the value +in the cell: + +| Instead of | Write | +|---|---| +| `\| Frameworks other than Express \| ❌ \|` | `\| Frameworks \| Express \|` | +| `\| Protocols besides MQTT v5 \| ❌ \|` | `\| Protocol version \| MQTT v5 \|` | + +And do not repeat a table row in prose further down — pick one home for the fact. + +## Write for someone with a task + +Every heading should name something the reader is trying to do or understand: +`Authentication`, `Base URL`, `Error handling`, `Path parameters`, +`Multi-status responses`. `docs/protocols/http_client.md` is the model — read it +before writing a new protocol page. + +State what the code does, and what the reader should do about it. If a +limitation matters, say what happens and what to do instead — the reader needs +the behaviour, not the reasoning that produced it. + +## Rewrites + +| Instead of | Write | +|---|---| +| "Explicit non-goals" / "Out of scope" / "Deliberate omissions" | Nothing — the support table already carries `❌` rows. If a `❌` needs elaboration, put it in the table cell: `❌ (compose your own middleware)` | +| "There is no `basePath` option because none is needed" | "Mounting under a prefix needs no extra configuration" | +| "`response` and `next` are deliberately not handed over: passing them would make the contract ambiguous" | "The callback's return value is the response. Reach for `request` when you need something unmodelled" | +| "The Ajv validator is compiled once, outside the route handler" | "Validation costs no per-request compilation" | +| "This was chosen over X because Y" | Delete. Design rationale belongs in the PR description or `.claude/thoughts/`, not `docs/` | +| "Known limitation" as a trailing section | Fold into the task-shaped section it affects, under a heading naming the situation ("Multiple response bodies") | + +## Before you finish + +- Does every `##` name a reader task, not a project decision? +- Does anything appear twice — once in the support table, once in prose? +- Would a sentence make sense to someone who never saw the PR? If it only makes + sense as an answer to a reviewer, cut it. +- Does it claim permanent intent ("never", "non-goal", "will not support") about + something that is merely unbuilt? Say what is supported today instead. +- Are the code samples real generator output? Never write them from memory — + run the CLI, or copy from `examples/` or `test/runtime/typescript/src/`. +- New page under `docs/protocols/`? Add it to the protocol lists in `README.md`, + `docs/README.md`, `docs/getting-started/protocols.md`, + `docs/generators/channels.md` and `docs/generators/README.md` — and run + `npm test -- --testPathPattern=protocol-surfaces`, which checks exactly that. diff --git a/.cursor/rules/protocols.mdc b/.cursor/rules/protocols.mdc index 9bc6c182..62246008 100644 --- a/.cursor/rules/protocols.mdc +++ b/.cursor/rules/protocols.mdc @@ -17,6 +17,7 @@ Current implementations: - **AMQP**: Enterprise messaging with exchanges/queues - **EventSource**: Server-sent events - **HTTP Client**: RESTful API communication +- **HTTP Server**: Typed Express handler stubs from an OpenAPI document (OpenAPI-only; shares the `http/` directory with the client) - **WebSocket**: Bidirectional communication ## Protocol File Structure diff --git a/.github/workflows/runtime-testing.yml b/.github/workflows/runtime-testing.yml index ced73574..383de12e 100644 --- a/.github/workflows/runtime-testing.yml +++ b/.github/workflows/runtime-testing.yml @@ -67,6 +67,10 @@ jobs: service: '' test-command: 'test:http' needs-service: false + - test-type: 'HTTP Server' + service: '' + test-command: 'test:http-server' + needs-service: false - test-type: 'Regular Tests' service: '' test-command: 'test:regular' diff --git a/CLAUDE.md b/CLAUDE.md index 63d69c94..fbff1698 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## What this is -`@the-codegen-project/cli` — an oclif-based CLI (binary name `codegen`) that reads API specification documents (AsyncAPI v2/v3, OpenAPI 2.0/3.0/3.1 + Swagger, JSON Schema Draft 4/6/7) and generates TypeScript code: payload/message models, parameter models, header models, general types, protocol channel helpers (NATS, Kafka, MQTT, AMQP, EventSource, HTTP client, WebSocket), and full clients. It is a standalone repo that happens to live inside the `platform-and-services` monorepo — it uses **npm** (not the monorepo's pnpm) and requires **Node.js 22+**. +`@the-codegen-project/cli` — an oclif-based CLI (binary name `codegen`) that reads API specification documents (AsyncAPI v2/v3, OpenAPI 2.0/3.0/3.1 + Swagger, JSON Schema Draft 4/6/7) and generates TypeScript code: payload/message models, parameter models, header models, general types, protocol channel helpers (NATS, Kafka, MQTT, AMQP, EventSource, HTTP client, HTTP server, WebSocket), and full clients. It is a standalone repo that happens to live inside the `platform-and-services` monorepo — it uses **npm** (not the monorepo's pnpm) and requires **Node.js 22+**. ## Commands @@ -75,4 +75,5 @@ The `.cursor/rules/*.mdc` files are the detailed, authoritative spec — read th - Use `Logger` from `src/LoggingInterface.ts`, never `console.log`. Avoid `any` without justification, hardcoded paths, and sync file ops in generators. - **MQTT channel code requires protocol v5** (user properties) and must topic-filter incoming messages — see `protocols.mdc`. - Conventional commits (`feat:`, `fix:`, `docs:`, …); releases are automated via semantic-release (`.releaserc`). -- No feature without docs + an example: update `docs/`, add to `examples/`, regenerate `schemas/`. +- No feature without docs + an example: update `docs/`, add to `examples/`, regenerate `schemas/`. Anything under `docs/` is written for the reader, not as a record of the work — no "non-goals", no design rationale, no capability rows phrased in negative space (`Frameworks other than Express ❌`). Use the `write-docs` skill; `docs/protocols/http_client.md` is the model page. +- **Protocol lists are duplicated across surfaces the generator doesn't own** — the `codegen init` wizard, the website playground and homepage, and the MCP server tool schemas each keep a hand-maintained copy. `test/protocol-surfaces.spec.ts` fails with the exact file and literal to add; the `add-protocol` skill has the full checklist. Never hand-edit the generated copies (`website/src/schemas/configuration-schema.json`, `website/static/codegen.browser.mjs`, `mcp-server/lib/resources/bundled-docs.ts`) — the release pipeline regenerates those. diff --git a/README.md b/README.md index 7aeb84ed..8ff53229 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ - 📊 Every generator fully customizable to fit your hearts desire - 👀 Integrate it into any project (Demos in [Next.JS](./examples/typescript-nextjs), [TypeScript Libraries](./examples/typescript-library)) - 💅 [Create custom generators to your use-case](https://the-codegen-project.org/docs/generators/custom) -- 🗄️ Protocol agnostic generator ([NATS](https://the-codegen-project.org/docs/protocols/nats), [Kafka](https://the-codegen-project.org/docs/protocols/kafka), [MQTT](https://the-codegen-project.org/docs/protocols/mqtt), [AMQP](https://the-codegen-project.org/docs/protocols/amqp), [event-source](https://the-codegen-project.org/docs/protocols/eventsource), [HTTP Client](https://the-codegen-project.org/docs/protocols/http_client), [WebSocket](https://the-codegen-project.org/docs/protocols/websocket), read the [docs](https://the-codegen-project.org/docs#protocols) for the full list and information) +- 🗄️ Protocol agnostic generator ([NATS](https://the-codegen-project.org/docs/protocols/nats), [Kafka](https://the-codegen-project.org/docs/protocols/kafka), [MQTT](https://the-codegen-project.org/docs/protocols/mqtt), [AMQP](https://the-codegen-project.org/docs/protocols/amqp), [event-source](https://the-codegen-project.org/docs/protocols/eventsource), [HTTP Client](https://the-codegen-project.org/docs/protocols/http_client), [HTTP Server](https://the-codegen-project.org/docs/protocols/http_server), [WebSocket](https://the-codegen-project.org/docs/protocols/websocket), read the [docs](https://the-codegen-project.org/docs#protocols) for the full list and information) - ⭐ And much more... # How it works diff --git a/docs/README.md b/docs/README.md index 940cc50b..ca3e585d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -56,6 +56,7 @@ Each protocol has its own limitations, corner cases, and features; thus, each ha - [MQTT](./protocols/mqtt.md) - [EventSource](./protocols/eventsource.md) - [HTTP Client](./protocols/http_client.md) +- [HTTP Server](./protocols/http_server.md) - [WebSocket client and server](./protocols/websocket.md) ### [Migrations](./migrations/README.md) diff --git a/docs/generators/README.md b/docs/generators/README.md index 02044fd6..52ecd5c2 100644 --- a/docs/generators/README.md +++ b/docs/generators/README.md @@ -24,7 +24,7 @@ All available generators, across languages and inputs: | OpenAPI | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | JSON Schema | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | -> OpenAPI `channels` and `client` generate an HTTP client — see the [`openapi-http-client` example](https://github.com/the-codegen-project/cli/tree/main/examples/openapi-http-client). +> OpenAPI `channels` and `client` generate an HTTP client — see the [`openapi-http-client` example](https://github.com/the-codegen-project/cli/tree/main/examples/openapi-http-client). OpenAPI `channels` can also generate the *server* side with the [`http_server`](../protocols/http_server.md) protocol — see the [`openapi-http-server` example](https://github.com/the-codegen-project/cli/tree/main/examples/openapi-http-server). | **Languages** | [`payloads`](./payloads.md) | [`parameters`](./parameters.md) | [`headers`](./headers.md) | [`types`](./types.md) | [`channels`](./channels.md) | [`client`](./client.md) | [`models`](./models.md) | [`custom`](./custom.md) | |---|---|---|---|---|---|---|---|---| diff --git a/docs/generators/channels.md b/docs/generators/channels.md index 02277598..e5aba965 100644 --- a/docs/generators/channels.md +++ b/docs/generators/channels.md @@ -28,7 +28,7 @@ This is supported through the following inputs: [`asyncapi`](../inputs/asyncapi. It supports the following languages; [`typescript`](#typescript) -It supports the following protocols; [`nats`](../protocols/nats.md), [`kafka`](../protocols/kafka.md), [`mqtt`](../protocols/mqtt.md), [`amqp`](../protocols/amqp.md), [`event_source`](../protocols/eventsource.md), [`http_client`](../protocols/http_client.md), [`websocket`](../protocols/websocket.md) +It supports the following protocols; [`nats`](../protocols/nats.md), [`kafka`](../protocols/kafka.md), [`mqtt`](../protocols/mqtt.md), [`amqp`](../protocols/amqp.md), [`event_source`](../protocols/eventsource.md), [`http_client`](../protocols/http_client.md), [`http_server`](../protocols/http_server.md), [`websocket`](../protocols/websocket.md) ## Options These are the available options for the `channels` generator; @@ -53,7 +53,7 @@ Depending on which protocol, these are the dependencies: - `MQTT`: https://github.com/mqttjs/MQTT.js v5 - `AMQP`: https://github.com/amqp-node/amqplib v0 - `EventSource`: `event_source_fetch`: https://github.com/Azure/fetch-event-source v2, `event_source_express`: https://github.com/expressjs/express v4 -- `HTTP`: none — uses the global `fetch` built into Node.js 18+ (the generated client relies on the native `fetch`/`Headers`; swap in `node-fetch`, `axios`, etc. via the `makeRequest` hook if needed) +- `HTTP`: `http_client`: none — uses the global `fetch` built into Node.js 18+ (the generated client relies on the native `fetch`/`Headers`; swap in `node-fetch`, `axios`, etc. via the `makeRequest` hook if needed), `http_server`: https://github.com/expressjs/express v4 - `WebSocket`: https://github.com/websockets/ws v8 For TypeScript, the generator creates one file per protocol plus an index file that re-exports all protocols as namespaces. For example; diff --git a/docs/getting-started/protocols.md b/docs/getting-started/protocols.md index 4e8a8027..b9af26bd 100644 --- a/docs/getting-started/protocols.md +++ b/docs/getting-started/protocols.md @@ -22,6 +22,7 @@ The Codegen Project currently supports these messaging protocols: | **[AMQP](../protocols/amqp.md)** | Advanced Message Queuing Protocol | Enterprise messaging, reliable message delivery | | **[EventSource](../protocols/eventsource.md)** | Server-Sent Events (SSE) protocol | Real-time web updates, streaming data to browsers | | **[HTTP Client](../protocols/http_client.md)** | RESTful API communication | HTTP APIs, REST services | +| **[HTTP Server](../protocols/http_server.md)** | Typed Express handler stubs from an OpenAPI document | Implementing a REST API against its specification | | **[WebSocket](../protocols/websocket.md)** | Full-duplex communication protocol | Real-time web applications, bidirectional communication | ## How Protocol Support Works diff --git a/docs/protocols/http_server.md b/docs/protocols/http_server.md new file mode 100644 index 00000000..d9ce0e9c --- /dev/null +++ b/docs/protocols/http_server.md @@ -0,0 +1,176 @@ +--- +sidebar_position: 99 +--- + +# HTTP Server + +The HTTP server generator creates typed [Express](https://expressjs.com/) handler stubs from your API specification — the structural inverse of the [HTTP client](./http_client.md). For every operation you get a `register(context)` function that mounts a route on a `Router` you supply, hands your handler typed path/query parameters, a typed request body and typed request headers, and marshals whatever your handler returns. + +It is currently available through the generators ([channels](../generators/channels.md)). + +This is available **only from [OpenAPI](../inputs/openapi.md) documents** (Swagger 2.0, OpenAPI 3.0 and 3.1). AsyncAPI input generates nothing for this protocol. + +## TypeScript + +Dependency: [express](https://github.com/expressjs/express) v4. + +| **Feature** | Is supported? | +|---|---| +| Typed path & query parameters | ✅ | +| Typed request headers | ✅ | +| Typed request body | ✅ | +| Typed, status-code-discriminated responses | ✅ | +| Request payload validation | ✅ (via the [`payloads`](../generators/payloads.md) generator's `includeValidation`) | +| Handler hooks (before/after/onError) | ✅ | +| Router mounting under a prefix | ✅ | +| JSON based API | ✅ | +| XML based API | ❌ | +| Authentication / authorization enforcement | ❌ (see [Security requirements](#security-requirements)) | +| Typed response headers | ❌ | +| Frameworks | Express | +| Server / listener construction | ❌ (you construct and mount the router) | +| POST / GET / PUT / PATCH / DELETE / HEAD / OPTIONS | ✅ | + +## Configuration + +```js +export default { + inputType: 'openapi', + inputPath: './openapi.json', + generators: [ + { + preset: 'channels', + outputPath: './src/__gen__/channels', + language: 'typescript', + protocols: ['http_server'] + } + ] +}; +``` + +`http_server` and `http_client` can be listed together. They produce two independent files, each with its own copy of the shared types (including `HttpError`), so generating both sides of the same document is a supported and useful setup — see the [`openapi-http-server` example](https://github.com/the-codegen-project/cli/tree/main/examples/openapi-http-server). + +## What is generated + +For each operation, three things: + +| Generated | What it is | +|---|---| +| `ServerResponse` | A status-code-discriminated union of everything the operation may answer with, assembled from the operation's declared `responses`. | +| `RegisterContext` | The register function's argument: `router`, your `callback`, and the shared `HttpServerContext` options. | +| `register(context)` | Mounts the route and handles parsing, parameter extraction, header deserialization, validation, response marshalling and error mapping. | + +```ts +export type GetPetByIdServerResponse = + | {status: 200; body: APetInterface | APet; headers?: Record} + | {status: 404; headers?: Record}; + +export interface RegisterGetPetByIdContext extends HttpServerContext { + router: Router; + callback: (params: { + parameters: GetPetByIdParameters; + requestHeaders: GetPetByIdHeaders; + request: Request; + }) => GetPetByIdServerResponse | Promise; +} +``` + +Using it: + +```ts +import {Router} from 'express'; +import {registerGetPetById} from './__gen__/channels/http_server'; + +const router = Router(); + +registerGetPetById({ + router, + callback: ({parameters}) => { + const pet = petStore.get(parameters.petId); + if (!pet) { + return {status: 404}; // declared by the document + } + return {status: 200, body: pet}; + } +}); +``` + +Returning a status the document does not declare is a **compile error**, not a runtime surprise. + +### The handler callback + +The callback takes a single destructured object: + +| Field | When it is present | +|---|---| +| `body` | Body-carrying methods (`POST`, `PUT`, `PATCH`) whose request body has a JSON schema. Already unmarshalled into the payload model. | +| `parameters` | The operation declares path or query parameters. A parsed instance of the generated parameter model. | +| `requestHeaders` | The operation declares header parameters. A typed object produced by the generated `deserializeHeaders`. | +| `request` | Always. The raw Express `Request`, for anything not modelled (cookies, raw auth headers, the socket). | + +The callback's return value is the response — `response` and `next` are not passed in. Use `request` for anything the models do not cover. + +The returned `body` accepts either a plain object literal or a model instance — object bodies are normalized to the model before `marshal()`, so the wire-name mapping is always applied. + +## `HttpServerContext` + +Every `RegisterContext` extends it: + +| Option | Type | Description | +|---|---|---| +| `additionalHeaders` | `Record` | Headers added to every response the route sends. A per-response `headers` field wins over these. | +| `hooks` | `HttpServerHooks` | `beforeHandler`, `afterHandler` and `onError` — see below. | +| `skipRequestValidation` | `boolean` | Skip validating the incoming request payload against its JSON Schema. | + +### Hooks + +```ts +export interface HttpServerHooks { + beforeHandler?: (params: {request: Request}) => void | Promise; + afterHandler?: (params: {request: Request; status: number; body?: string}) => void | Promise; + onError?: (params: {error: HttpGlobalError; request: Request}) => + {status: number; body?: unknown} | undefined | Promise<{status: number; body?: unknown} | undefined>; +} +``` + +`beforeHandler` runs before the request is read — throw an `HttpError` there to reject a request before it reaches your handler. `afterHandler` receives the JSON text that was sent. `onError` may return a replacement response, or `undefined` to keep the mapped one. + +## Errors + +Throw an `HttpError` (exported from the generated file) to answer with a specific status: + +```ts +throw new HttpError('pet is not for sale', 409, 'Conflict', {petId}); +``` + +Anything else you throw maps to a generic `500` — an internal error message is **never** leaked into the response body. If the response has already started (`headersSent`), the error is passed to Express' error middleware instead, so mount one if you need to observe those. + +`HttpError` is shape-compatible with the one the generated HTTP client throws, so the same class reads the same on both sides of the wire. + +## Request validation + +When the [`payloads`](../generators/payloads.md) generator has `includeValidation` enabled (the default), request bodies are validated against their JSON Schema before your callback runs. A failing body is answered with `400` and the validation causes. Validation costs no per-request compilation — the validator is built when the route is registered. + +There is no separate configuration option — set `skipRequestValidation: true` on the context to turn it off per route. + +## Mounting and prefixes + +Generated code never calls `app.listen`, never constructs a `Router` and never mounts anything: + +```ts +const app = express(); +app.use(express.json()); // optional — the stubs also read the raw stream +app.use('/api/v2', router); +``` + +Mounting under a prefix needs no extra configuration — there is no `basePath` option. Express makes `request.url` mount-relative, so path parameters resolve the same whether the router is mounted at `/` or at `/api/v2`. + +`express.json()` is optional. `readJsonBody` returns an already-parsed body when a body parser populated one, and otherwise reads the raw stream itself. + +## Multiple response bodies + +When an operation declares several body-carrying responses, the response payload becomes a *union* model. A non-object member of that union — an array or a primitive — has no importable module of its own, so its body is marshalled with `JSON.stringify` rather than the model's `marshal()`, and no wire-name mapping is applied to that variant. If the operation's schemas rename properties on the wire, return an object variant for those responses. Single-response operations and object union members are unaffected. + +## Security requirements + +Per-operation `security` requirements are read from the document but not enforced: no credential verification is generated. Mount your own middleware on the router before the generated routes, and throw an [`HttpError`](#errors) from it to reject a request. diff --git a/docs/usage.md b/docs/usage.md index 61018564..61877f63 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -123,16 +123,17 @@ USAGE $ codegen init [--json] [--no-color] [--debug | | [--silent | -v | -q]] [--help] [--input-file ] [--config-name ] [--input-type asyncapi|openapi|jsonschema] [--output-directory ] [--config-type esm|json|yaml|ts] [--languages typescript] [--channels-protocols - nats|kafka|mqtt|amqp|event_source|http_client|websocket] [--no-tty] [--include-payloads] [--include-headers] - [--include-client] [--include-parameters] [--include-channels] [--include-types] [--include-models] - [--gitignore-generated] + nats|kafka|mqtt|amqp|event_source|http_client|websocket|http_server] [--no-tty] [--include-payloads] + [--include-headers] [--include-client] [--include-parameters] [--include-channels] [--include-types] + [--include-models] [--gitignore-generated] FLAGS -q, --quiet Only show errors and warnings -v, --verbose Show detailed output --channels-protocols=