@trpc/openapi 11.14.0-alpha → 11.14.1-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -38,6 +38,14 @@ The generator statically analyses your router's TypeScript types — it never ex
38
38
 
39
39
  Full documentation is available at [trpc.io/docs/openapi](https://trpc.io/docs/openapi).
40
40
 
41
+ ## AI Agents
42
+
43
+ If you use an AI coding agent, install tRPC skills for better code generation:
44
+
45
+ ```bash
46
+ npx @tanstack/intent@latest install
47
+ ```
48
+
41
49
  ## TODO
42
50
 
43
51
  - [ ] SSE subscriptions
package/bin/intent.js ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ // Auto-generated by @tanstack/intent setup
3
+ // Exposes the intent end-user CLI for consumers of this library.
4
+ // Commit this file, then add to your package.json:
5
+ // "bin": { "intent": "./bin/intent.js" }
6
+ try {
7
+ await import('@tanstack/intent/intent-library');
8
+ } catch (e) {
9
+ if (e?.code === 'ERR_MODULE_NOT_FOUND' || e?.code === 'MODULE_NOT_FOUND') {
10
+ console.error('@tanstack/intent is not installed.');
11
+ console.error('');
12
+ console.error('Install it as a dev dependency:');
13
+ console.error(' npm add -D @tanstack/intent');
14
+ console.error('');
15
+ console.error('Or run directly:');
16
+ console.error(' npx @tanstack/intent@latest list');
17
+ process.exit(1);
18
+ }
19
+ throw e;
20
+ }
package/package.json CHANGED
@@ -2,12 +2,13 @@
2
2
  "name": "@trpc/openapi",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "11.14.0-alpha",
5
+ "version": "11.14.1-canary.0+e39a654b3",
6
6
  "description": "OpenAPI document generator for tRPC routers",
7
7
  "author": "KATT",
8
8
  "license": "MIT",
9
9
  "bin": {
10
- "trpc-openapi": "./dist/cli.js"
10
+ "trpc-openapi": "./dist/cli.js",
11
+ "intent": "./bin/intent.js"
11
12
  },
12
13
  "homepage": "https://trpc.io",
13
14
  "repository": {
@@ -64,7 +65,10 @@
64
65
  "!**/*.test.*",
65
66
  "!**/__tests__",
66
67
  "!test",
67
- "heyapi"
68
+ "heyapi",
69
+ "skills",
70
+ "bin",
71
+ "!skills/_artifacts"
68
72
  ],
69
73
  "publishConfig": {
70
74
  "access": "public"
@@ -72,7 +76,8 @@
72
76
  "devDependencies": {
73
77
  "@hey-api/openapi-ts": "^0.94.1",
74
78
  "@swagger-api/apidom-ls": "^1.6.0",
75
- "@trpc/server": "11.14.0",
79
+ "@tanstack/intent": "^0.0.20",
80
+ "@trpc/server": "11.14.1",
76
81
  "@types/node": "^22.13.5",
77
82
  "bson": "^7.2.0",
78
83
  "eslint": "^9.26.0",
@@ -85,7 +90,7 @@
85
90
  },
86
91
  "peerDependencies": {
87
92
  "@hey-api/openapi-ts": ">=0.13.0",
88
- "@trpc/server": "11.14.0",
93
+ "@trpc/server": "11.14.1",
89
94
  "typescript": ">=5.7.2",
90
95
  "zod": ">=4.0.0"
91
96
  },
@@ -100,5 +105,8 @@
100
105
  "funding": [
101
106
  "https://trpc.io/sponsor"
102
107
  ],
103
- "gitHead": "6e03f5c2f8d8ebaa237747d2db447737393402c6"
108
+ "keywords": [
109
+ "tanstack-intent"
110
+ ],
111
+ "gitHead": "e39a654b306d0c75730a9c546e77333f9d1dca8c"
104
112
  }
@@ -0,0 +1,300 @@
1
+ ---
2
+ name: openapi
3
+ description: >
4
+ Generate OpenAPI 3.1 spec from a tRPC router with @trpc/openapi CLI or
5
+ programmatic API. Generate typed REST client with @hey-api/openapi-ts and
6
+ configureTRPCHeyApiClient(). Configure transformers (superjson, EJSON) for
7
+ generated clients. Alpha status.
8
+ type: composition
9
+ library: trpc
10
+ library_version: '11.14.0-alpha'
11
+ requires:
12
+ - server-setup
13
+ sources:
14
+ - 'trpc/trpc:www/docs/client/openapi.md'
15
+ - 'trpc/trpc:packages/openapi/test/heyapi.test.ts'
16
+ - 'trpc/trpc:examples/openapi-codegen/'
17
+ ---
18
+
19
+ # tRPC -- OpenAPI
20
+
21
+ > **Alpha**: `@trpc/openapi` is versioned as `11.x.x-alpha`. APIs may change without notice.
22
+
23
+ ## Setup
24
+
25
+ ### 1. Install
26
+
27
+ ```bash
28
+ pnpm add @trpc/openapi
29
+ ```
30
+
31
+ For HeyAPI client generation:
32
+
33
+ ```bash
34
+ pnpm add @hey-api/openapi-ts -D
35
+ ```
36
+
37
+ ### 2. Generate the OpenAPI spec
38
+
39
+ The generator statically analyses your router's TypeScript types. It never executes your code.
40
+
41
+ **CLI:**
42
+
43
+ ```bash
44
+ pnpm exec trpc-openapi ./src/server/index.ts -e appRouter -o openapi.json --title "My API" --version 1.0.0
45
+ ```
46
+
47
+ | Option | Default | Description |
48
+ | --------------------- | -------------- | --------------------------- |
49
+ | `-e, --export <name>` | `AppRouter` | Name of the exported router |
50
+ | `-o, --output <file>` | `openapi.json` | Output file path |
51
+ | `--title <text>` | `tRPC API` | OpenAPI `info.title` |
52
+ | `--version <ver>` | `0.0.0` | OpenAPI `info.version` |
53
+
54
+ **Programmatic:**
55
+
56
+ ```ts
57
+ import { generateOpenAPIDocument } from '@trpc/openapi';
58
+
59
+ const doc = await generateOpenAPIDocument('./src/server/index.ts', {
60
+ exportName: 'appRouter',
61
+ title: 'My API',
62
+ version: '1.0.0',
63
+ });
64
+ ```
65
+
66
+ ### 3. Generate a HeyAPI client from the spec
67
+
68
+ ```ts
69
+ // scripts/codegen.ts
70
+ import { rmSync, writeFileSync } from 'node:fs';
71
+ import * as path from 'node:path';
72
+ import { fileURLToPath } from 'node:url';
73
+ import { createClient } from '@hey-api/openapi-ts';
74
+ import { generateOpenAPIDocument } from '@trpc/openapi';
75
+ import { createTRPCHeyApiTypeResolvers } from '@trpc/openapi/heyapi';
76
+
77
+ const __filename = fileURLToPath(import.meta.url);
78
+ const __dirname = path.dirname(__filename);
79
+
80
+ const routerPath = path.resolve(__dirname, '..', 'server', 'index.ts');
81
+ const outputDir = path.resolve(__dirname, '..', 'client', 'generated');
82
+ const specPath = path.resolve(__dirname, '..', '..', 'openapi.json');
83
+
84
+ async function main() {
85
+ const doc = await generateOpenAPIDocument(routerPath, {
86
+ exportName: 'appRouter',
87
+ title: 'Example API',
88
+ version: '1.0.0',
89
+ });
90
+
91
+ writeFileSync(specPath, JSON.stringify(doc, null, 2) + '\n');
92
+
93
+ rmSync(outputDir, { recursive: true, force: true });
94
+
95
+ await createClient({
96
+ input: specPath,
97
+ output: outputDir,
98
+ plugins: [
99
+ {
100
+ name: '@hey-api/typescript',
101
+ '~resolvers': createTRPCHeyApiTypeResolvers(),
102
+ },
103
+ {
104
+ name: '@hey-api/sdk',
105
+ operations: { strategy: 'single' },
106
+ },
107
+ ],
108
+ });
109
+ }
110
+
111
+ main().catch((err) => {
112
+ console.error(err);
113
+ process.exit(1);
114
+ });
115
+ ```
116
+
117
+ Run it:
118
+
119
+ ```bash
120
+ pnpm tsx scripts/codegen.ts
121
+ ```
122
+
123
+ ### 4. Configure and use the generated client at runtime
124
+
125
+ ```ts
126
+ import { configureTRPCHeyApiClient } from '@trpc/openapi/heyapi';
127
+ import { client } from './generated/client.gen';
128
+ import { Sdk } from './generated/sdk.gen';
129
+
130
+ configureTRPCHeyApiClient(client, {
131
+ baseUrl: 'http://localhost:3000',
132
+ });
133
+ const sdk = new Sdk({ client });
134
+
135
+ // Queries -> GET, Mutations -> POST
136
+ const result = await sdk.greeting({ query: { input: { name: 'World' } } });
137
+ const user = await sdk.user.create({ body: { name: 'Bob', age: 30 } });
138
+ ```
139
+
140
+ ## Core Patterns
141
+
142
+ ### CLI quick spec generation
143
+
144
+ ```bash
145
+ # Default export name "AppRouter", output "openapi.json"
146
+ pnpm exec trpc-openapi ./src/server/router.ts
147
+
148
+ # Custom export name and output
149
+ pnpm exec trpc-openapi ./src/server/router.ts -e appRouter -o api.json --title "My API" --version 1.0.0
150
+ ```
151
+
152
+ ### HeyAPI codegen with type resolvers (transformer setup)
153
+
154
+ When the server uses a transformer, pass `createTRPCHeyApiTypeResolvers()` to the `@hey-api/typescript` plugin so generated types use `Date` instead of `string` for date-time fields and `bigint` for bigint fields:
155
+
156
+ ```ts
157
+ import { createClient } from '@hey-api/openapi-ts';
158
+ import { createTRPCHeyApiTypeResolvers } from '@trpc/openapi/heyapi';
159
+
160
+ await createClient({
161
+ input: './openapi.json',
162
+ output: './generated',
163
+ plugins: [
164
+ {
165
+ name: '@hey-api/typescript',
166
+ '~resolvers': createTRPCHeyApiTypeResolvers(),
167
+ },
168
+ {
169
+ name: '@hey-api/sdk',
170
+ operations: { strategy: 'single' },
171
+ },
172
+ ],
173
+ });
174
+ ```
175
+
176
+ ### Runtime client with superjson transformer
177
+
178
+ When the tRPC server uses `superjson`, the client must be configured with the same transformer:
179
+
180
+ ```ts
181
+ // src/shared/transformer.ts
182
+ import superjson from 'superjson';
183
+
184
+ export const transformer = superjson;
185
+ ```
186
+
187
+ ```ts
188
+ // src/server/trpc.ts
189
+ import { initTRPC } from '@trpc/server';
190
+ import { transformer } from '../shared/transformer';
191
+
192
+ const t = initTRPC.create({ transformer });
193
+ export const router = t.router;
194
+ export const publicProcedure = t.procedure;
195
+ ```
196
+
197
+ ```ts
198
+ // src/client/index.ts
199
+ import { configureTRPCHeyApiClient } from '@trpc/openapi/heyapi';
200
+ import superjson from 'superjson';
201
+ import { client } from './generated/client.gen';
202
+ import { Sdk } from './generated/sdk.gen';
203
+
204
+ configureTRPCHeyApiClient(client, {
205
+ baseUrl: 'http://localhost:3000',
206
+ transformer: superjson,
207
+ });
208
+ const sdk = new Sdk({ client });
209
+
210
+ const event = await sdk.getEvent({
211
+ query: { input: { id: 'evt_1', at: new Date('2025-06-15T10:00:00Z') } },
212
+ });
213
+ // event.data.result.data.at is a Date object
214
+ ```
215
+
216
+ ### MongoDB EJSON transformer (cross-language)
217
+
218
+ For non-TypeScript clients, EJSON provides a language-agnostic serialization format:
219
+
220
+ ```ts
221
+ import type { TRPCDataTransformer } from '@trpc/server';
222
+ import type { Document } from 'bson';
223
+ import { EJSON } from 'bson';
224
+
225
+ export const ejsonTransformer: TRPCDataTransformer = {
226
+ serialize: (value) => EJSON.serialize(value),
227
+ deserialize: (value) => EJSON.deserialize(value as Document),
228
+ };
229
+ ```
230
+
231
+ ```ts
232
+ import { configureTRPCHeyApiClient } from '@trpc/openapi/heyapi';
233
+ import { client } from './generated/client.gen';
234
+ import { ejsonTransformer } from './transformer';
235
+
236
+ configureTRPCHeyApiClient(client, {
237
+ baseUrl: 'http://localhost:3000',
238
+ transformer: ejsonTransformer,
239
+ });
240
+ ```
241
+
242
+ ### Response shape
243
+
244
+ All tRPC HTTP responses follow the envelope format. Access data through `result.data`:
245
+
246
+ ```ts
247
+ const listResult = await sdk.user.list();
248
+ const users = listResult.data?.result.data; // the actual return value
249
+
250
+ const createResult = await sdk.user.create({ body: { name: 'nick' } });
251
+ const user = createResult.data?.result.data;
252
+ // user.createdAt instanceof Date === true (when transformer is configured)
253
+ ```
254
+
255
+ ### Descriptions in the spec
256
+
257
+ Zod `.describe()` calls and JSDoc comments on types, routers, and procedures become `description` fields in the generated OpenAPI spec. No annotations or decorators required.
258
+
259
+ ## Common Mistakes
260
+
261
+ ### Missing transformer config in HeyAPI client
262
+
263
+ When the tRPC server uses `superjson` or another transformer, the generated HeyAPI client must also be configured with the same transformer via `configureTRPCHeyApiClient(client, { transformer })`. Without this, `Date`, `Map`, `Set`, and other non-JSON types will be silently wrong at runtime -- they arrive as raw serialized objects instead of their native types.
264
+
265
+ Wrong:
266
+
267
+ ```ts
268
+ configureTRPCHeyApiClient(client, {
269
+ baseUrl: 'http://localhost:3000',
270
+ // missing transformer -- Dates will be broken
271
+ });
272
+ ```
273
+
274
+ Right:
275
+
276
+ ```ts
277
+ configureTRPCHeyApiClient(client, {
278
+ baseUrl: 'http://localhost:3000',
279
+ transformer: superjson, // must match server's transformer
280
+ });
281
+ ```
282
+
283
+ ### Expecting subscriptions in OpenAPI spec
284
+
285
+ Subscriptions are currently excluded from OpenAPI spec generation. The generator silently skips any procedure with `type: 'subscription'`. SSE subscription support is planned but not yet available.
286
+
287
+ ### Forgetting createTRPCHeyApiTypeResolvers when using a transformer
288
+
289
+ Without the type resolvers plugin, HeyAPI generates `string` types for date-time fields instead of `Date`. The `createTRPCHeyApiTypeResolvers()` function maps `date`/`date-time` format to `Date` and `bigint` format to `bigint` in the generated TypeScript SDK.
290
+
291
+ ### Using the wrong export name
292
+
293
+ The CLI defaults to `--export AppRouter` (the type). If your file exports the router value as `appRouter`, pass `-e appRouter`. If the export is not found, the error message lists all available exports from the file.
294
+
295
+ ## See Also
296
+
297
+ - **server-setup** -- Required. Define routers and procedures before generating the spec.
298
+ - **superjson** -- Transformer configuration for server and client. OpenAPI clients need matching transformer config.
299
+ - **validators** -- Zod `.describe()` calls propagate into OpenAPI `description` fields.
300
+ - Full working example: `examples/openapi-codegen/`