Skip to content

Commit 74364e5

Browse files
authored
feat: add openapi typescript client (#2885)
feat(openapi-typescript): added generate-client
1 parent 3016d43 commit 74364e5

414 files changed

Lines changed: 45763 additions & 231 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/client-generator.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@redocly/client-generator': minor
3+
'@redocly/openapi-core': minor
4+
'@redocly/cli': minor
5+
---
6+
7+
Added an experimental `generate-client` command that generates a typed, zero-dependency TypeScript client from an OpenAPI description — auth, retries, middleware, typed SSE streaming, pagination, and multipart included — plus optional companion generators for Zod validation, TanStack Query and SWR hooks, MSW mocks, and date transformers.
8+
See the [`generate-client` command reference](https://redocly.com/docs/cli/commands/generate-client) and the [Use the generated client](https://redocly.com/docs/cli/guides/use-generated-client) guide.

.claude/skills/redocly-cli/SKILL.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: redocly-cli
3-
description: Redocly CLI usage for OpenAPI, AsyncAPI, Arazzo, and Overlay descriptions. Use when linting an API description, bundling or splitting multi-file descriptions, joining several APIs into one, transforming a description with decorators, building or previewing API docs, testing a live API with respect to its description.
3+
description: Redocly CLI usage for OpenAPI, AsyncAPI, Arazzo, and Overlay descriptions. Use when linting an API description, bundling or splitting multi-file descriptions, joining several APIs into one, transforming a description with decorators, building or previewing API docs, testing a live API with respect to its description, generating a TypeScript client from an OpenAPI description.
44
---
55

66
# Redocly CLI usage
@@ -38,6 +38,7 @@ Install: `npm i @redocly/cli@latest`, or run without installing: `npx @redocly/c
3838
| `preview` | Local preview of a Redocly project |
3939
| `respect` | Run API tests described in an Arazzo description against a live API |
4040
| `generate-arazzo` | Scaffold an Arazzo description from an OpenAPI description |
41+
| `generate-client` | Generate a typed, zero-dependency TypeScript client [experimental] |
4142
| `login` / `logout` / `push` / `push-status` | Authenticate and push to the Redocly platform (Reunite) |
4243

4344
Exit codes: `0` success, `1` problems found or execution failed, `2` configuration error.
@@ -118,6 +119,29 @@ rules:
118119
my-plugin/operation-id-not-test: error
119120
```
120121

122+
## Generate a TypeScript client
123+
124+
`generate-client <api> --output client.ts` turns an OpenAPI description into a typed client — one self-contained file with zero runtime dependencies (auth, retries, middleware, typed SSE, pagination included).
125+
Configure it durably under a `client` block in `redocly.yaml` instead of flags:
126+
127+
```yaml
128+
client:
129+
generators: [sdk, zod] # add-ons: tanstack-query, swr, mock, transformers, or a plugin path
130+
outputMode: split
131+
pagination: # config-only, no CLI flag
132+
style: cursor
133+
cursorParam: after
134+
nextCursor: /page/endCursor
135+
items: /items
136+
apis:
137+
my-api:
138+
root: ./openapi/openapi.yaml
139+
clientOutput: ./src/api/client.ts
140+
```
141+
142+
With `apis.<name>.clientOutput` set, a bare `redocly generate-client` generates every opted-in API.
143+
An API's own `client` block replaces the top-level one wholesale — repeat the shared fields in it.
144+
121145
## Test a live API
122146

123147
`respect` executes an [Arazzo](https://spec.openapis.org/arazzo/latest.html) description as a test suite against a running API, asserting real responses match the description.
@@ -139,6 +163,7 @@ Start from `generate-arazzo <openapi>` to scaffold the workflows, then refine th
139163
- `respect` currently covers only synchronous HTTP flow.
140164
- Any `redocly.yaml` in the working directory configures every command — a stray one changes lint results silently.
141165
- `--extends` on the command line sets the base ruleset for that run; useful for a quick `--extends=spec` conformance check.
166+
- `generate-client` needs the `typescript` package (6.x) available at generation time; the generated client itself compiles with any TypeScript, including 7.
142167

143168
## Resources
144169

.github/workflows/release.yaml

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,11 @@ jobs:
206206
cache: npm
207207
registry-url: https://registry.npmjs.org
208208

209+
# Install before the version bump: npm links workspace packages only while
210+
# their versions still match the dependency specs in package.json files.
211+
- name: Install dependencies
212+
run: npm install
213+
209214
- name: Update package versions
210215
run: |
211216
TIMESTAMP=$(date +%s)
@@ -218,10 +223,14 @@ jobs:
218223
jq ".version = \"$VERSION\"" packages/respect-core/package.json > tmp.json && mv tmp.json packages/respect-core/package.json
219224
jq ".dependencies[\"@redocly/openapi-core\"] = \"$VERSION\"" packages/respect-core/package.json > tmp.json && mv tmp.json packages/respect-core/package.json
220225
226+
# Update Client Generator package version and dependencies
227+
jq ".version = \"$VERSION\"" packages/client-generator/package.json > tmp.json && mv tmp.json packages/client-generator/package.json
228+
jq ".dependencies[\"@redocly/openapi-core\"] = \"$VERSION\"" packages/client-generator/package.json > tmp.json && mv tmp.json packages/client-generator/package.json
229+
221230
# Update CLI package version and dependencies, so npm links the local
222231
# workspace packages instead of installing the published versions
223232
jq ".version = \"$VERSION\"" packages/cli/package.json > tmp.json && mv tmp.json packages/cli/package.json
224-
jq ".devDependencies[\"@redocly/openapi-core\"] = \"$VERSION\" | .devDependencies[\"@redocly/respect-core\"] = \"$VERSION\"" packages/cli/package.json > tmp.json && mv tmp.json packages/cli/package.json
233+
jq ".devDependencies[\"@redocly/openapi-core\"] = \"$VERSION\" | .devDependencies[\"@redocly/respect-core\"] = \"$VERSION\" | .devDependencies[\"@redocly/client-generator\"] = \"$VERSION\"" packages/cli/package.json > tmp.json && mv tmp.json packages/cli/package.json
225234
226235
# Add comment with installation instructions
227236
COMMENT="📦 A new experimental 🧪 version **v$VERSION** of Redocly CLI has been published for testing.
@@ -238,9 +247,6 @@ jobs:
238247
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
239248
PR_NUMBER: ${{ github.event.pull_request.number }}
240249

241-
- name: Install dependencies
242-
run: npm install
243-
244250
- name: Build packages
245251
run: npm run compile
246252

@@ -260,3 +266,7 @@ jobs:
260266
npm run prepare:publish-dir
261267
npm publish ./.publish --tag snapshot
262268
npm run clean:publish-dir
269+
sleep 10
270+
271+
cd ../client-generator
272+
npm publish --tag snapshot

.github/workflows/tests.yaml

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ on:
77
branches:
88
- main
99

10+
permissions:
11+
contents: read
12+
1013
env:
1114
CI: true
1215
REDOCLY_TELEMETRY: off
@@ -32,8 +35,44 @@ jobs:
3235
continue-on-error: true # Do not fail if there is an error during reporting
3336
uses: davelosert/vitest-coverage-report-action@d63aa97db4c0319f304f1787689de1ca548365cf # v2.11.1
3437

35-
- name: E2E Tests
36-
run: npm run e2e
38+
e2e:
39+
# The e2e suite is split across shards so no single runner carries the whole set.
40+
# Running all suites in one step was cancelled mid-run by the Actions service once the
41+
# generate-client suites grew past ~28 (a healthy runner, no resource exhaustion);
42+
# each shard stays well under that.
43+
runs-on: ubuntu-latest
44+
strategy:
45+
fail-fast: false
46+
matrix:
47+
shard: [1, 2]
48+
steps:
49+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
50+
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
51+
with:
52+
node-version: 24
53+
cache: npm
54+
- name: Install dependencies
55+
run: npm ci
56+
- name: E2E Tests (shard ${{ matrix.shard }}/2)
57+
run: npm run e2e -- --shard=${{ matrix.shard }}/2
58+
59+
examples:
60+
# The examples gitignore their generated clients (only zero-install-quickstart commits
61+
# its canonical copy), so this job regenerates every client and type-checks the
62+
# hand-written consumer code against it.
63+
runs-on: ubuntu-latest
64+
steps:
65+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
66+
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
67+
with:
68+
node-version: 24
69+
cache: npm
70+
- name: Install dependencies
71+
run: npm ci
72+
- name: Regenerate example clients
73+
run: npm run examples:regen -w @redocly/client-generator
74+
- name: Type-check examples
75+
run: npm run typecheck:examples -w @redocly/client-generator
3776

3877
code-style-check:
3978
runs-on: ubuntu-latest

.oxfmtrc.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@
1010
"packages/respect-core/src/modules/runtime-expressions/abnf-parser.js",
1111
"packages/core/src/rules/common/__tests__/fixtures/invalid-yaml.yaml",
1212
"tests/performance/api-definitions/",
13+
"tests/e2e/generate-client/examples/*/src/api/",
14+
"tests/e2e/generate-client/*-consumer/api*.ts",
1315
"tests/smoke/**/*.yaml",
1416
"snapshot*.txt",
17+
"tests/e2e/generate-client/*.snapshot.ts",
1518
"*.html",
1619
"*.hbs",
1720
"*.toml",

.oxlintrc.json

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,15 @@
99
"builtin": true,
1010
"es2026": true
1111
},
12-
"ignorePatterns": ["packages/*/lib/**", "*.js", "*.cjs"],
12+
"ignorePatterns": [
13+
"packages/*/lib/**",
14+
"*.js",
15+
"*.cjs",
16+
"tests/e2e/generate-client/examples/*/src/api/**",
17+
"tests/e2e/generate-client/examples/*/generate.ts",
18+
"tests/e2e/generate-client/*.snapshot.ts",
19+
"tests/e2e/generate-client/*-consumer/api*.ts"
20+
],
1321
"rules": {
1422
"eslint/no-array-constructor": "error",
1523
"eslint/no-case-declarations": "error",
@@ -68,6 +76,15 @@
6876
"rules": {
6977
"eslint/no-console": "allow"
7078
}
79+
},
80+
{
81+
"files": [
82+
"tests/e2e/generate-client/examples/*/src/**",
83+
"tests/e2e/generate-client/examples/*/worker.ts"
84+
],
85+
"rules": {
86+
"eslint/no-console": "allow"
87+
}
7188
}
7289
]
7390
}

CONTRIBUTING.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,11 @@ To test local changes as a package, you can use the following steps:
199199
1. (Optional) Change the version of the packages.
200200

201201
1. Run `npm run pack:prepare` in the repository's root.
202-
This command generates **redocly-cli.tgz**, **respect-core.tgz**, and **openapi-core.tgz** files.
202+
This command generates **redocly-cli.tgz**, **respect-core.tgz**, **openapi-core.tgz**, and **client-generator.tgz** files.
203203

204204
1. Copy these **.tgz** files to a destination folder and then run `npm install redocly-cli.tgz` there to install Redocly CLI.
205205
To install `openapi-core`: repeat this step, but with **openapi-core.tgz** file.
206+
Apps that consume a `runtime: package` generated client install **client-generator.tgz** the same way.
206207

207208
## Tests
208209

@@ -213,6 +214,7 @@ Run `npm test` to start both unit and e2e tests (and additionally typecheck the
213214
### Unit tests
214215

215216
Run unit tests with this command: `npm run unit`.
217+
This command runs the suite for every package whose tests match the discovery glob — there is no per-package `npm test` script.
216218

217219
Unit tests in the **cli** package are sensitive to top-level configuration file (**redocly.yaml**).
218220

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,21 @@ Respect is Redocly's community-edition product. Looking for something more? We a
139139

140140
Learn more about [Respect](https://redocly.com/respect) and [get started with API contract testing](https://redocly.com/docs/respect/get-started).
141141

142+
### Generate a TypeScript client
143+
144+
> ⚠️ **Experimental** — flags, output, and configuration may change in any minor release until declared stable.
145+
146+
Turn an OpenAPI description (3.0/3.1/3.2 or Swagger 2.0) into a typed TypeScript client with the `generate-client` command.
147+
The emitted client has zero runtime dependencies and runs in browsers, Node, Bun, Deno, and edge runtimes.
148+
149+
```sh
150+
redocly generate-client openapi.yaml --output src/client.ts
151+
```
152+
153+
Inline types plus a typed client instance and one async function per operation, with auth, opt-in abort-aware retries, middleware, and typed Server-Sent Events.
154+
The same command can also emit Zod schemas, TanStack Query / SWR hooks, MSW mocks, and more via `--generator`.
155+
For detailed information, read the [ `generate-client` docs](./docs/@v2/commands/generate-client.md).
156+
142157
### Transform an OpenAPI description
143158

144159
If your OpenAPI description isn't everything you hoped it would be, enhance it with the Redocly [decorators](https://redocly.com/docs/cli/decorators) feature.

0 commit comments

Comments
 (0)