Skip to content

Commit b36dafc

Browse files
authored
feat(zod): shallow merge JSON schema registries & dedicated converter integration docs (#1696)
## Summary - Extracts the JSON Schema converter sections from the OpenAPI Specification page into dedicated integration docs: **Standard Schema**, **Zod**, **Valibot**, and **ArkType**, all added to the sidebar. The spec page keeps a short `Json Schema Converters` section that links out, so existing anchors still work. - `ZodToJsonSchemaConverter` now shallow merges `JSON_SCHEMA_REGISTRY` with `JSON_SCHEMA_INPUT_REGISTRY`/`JSON_SCHEMA_OUTPUT_REGISTRY` (direction-specific keys win) instead of ignoring the general registry when a direction entry exists. ```ts JSON_SCHEMA_REGISTRY.add(user, { description: 'A user' }) JSON_SCHEMA_INPUT_REGISTRY.add(user, { examples: [{ name: 'John', age: '20' }] }) // input schema now includes both the shared description and the input examples ``` ## Outcome - Each schema library has its own integration page, including the new Zod registries feature and the Standard JSON Schema fallback behavior. - Shared registry metadata no longer needs to be duplicated into both direction-specific registries.
1 parent 3826b44 commit b36dafc

8 files changed

Lines changed: 292 additions & 119 deletions

File tree

apps/content/.vitepress/config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,15 +186,19 @@ export default withMermaid(defineConfig({
186186
text: 'Integrations',
187187
collapsed: true,
188188
items: [
189+
{ text: 'ArkType', link: '/docs/integrations/arktype' },
189190
{ text: 'Effect', link: '/docs/integrations/effect' },
190191
{ text: 'Evlog', link: '/docs/integrations/evlog' },
191192
{ text: 'NestJS', link: '/docs/integrations/nest' },
192193
{ text: 'Next.js', link: '/docs/integrations/next' },
193194
{ text: 'OpenTelemetry', link: '/docs/integrations/opentelemetry' },
194195
{ text: 'Pinia Colada', link: '/docs/integrations/pinia-colada' },
195196
{ text: 'Pino', link: '/docs/integrations/pino' },
197+
{ text: 'Standard Schema', link: '/docs/integrations/standard-schema' },
196198
{ text: 'Tanstack Query', link: '/docs/integrations/tanstack-query' },
197199
{ text: 'tRPC', link: '/docs/integrations/trpc' },
200+
{ text: 'Valibot', link: '/docs/integrations/valibot' },
201+
{ text: 'Zod', link: '/docs/integrations/zod' },
198202
],
199203
},
200204
{
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# ArkType Integration
2+
3+
[ArkType](https://arktype.io/) implements [Standard Schema](/docs/integrations/standard-schema), so you can use it directly in your procedures without any extra setup. On top of that, `@orpc/arktype` provides a dedicated JSON Schema converter.
4+
5+
## Installation
6+
7+
::: code-group
8+
9+
```sh [npm]
10+
npm install @orpc/arktype@beta arktype
11+
```
12+
13+
```sh [yarn]
14+
yarn add @orpc/arktype@beta arktype
15+
```
16+
17+
```sh [pnpm]
18+
pnpm add @orpc/arktype@beta arktype
19+
```
20+
21+
```sh [bun]
22+
bun add @orpc/arktype@beta arktype
23+
```
24+
25+
```sh [deno]
26+
deno add npm:@orpc/arktype@beta npm:arktype
27+
```
28+
29+
:::
30+
31+
## JSON Schema Converter
32+
33+
`ArkTypeToJsonSchemaConverter` wraps [ArkType's built-in toJsonSchema](https://arktype.io/docs/type-api#tojsonschema) and adds support for additional types such as `bigint` and `Date`. Use it with tools such as the [OpenAPI Generator](/docs/openapi/specification#openapi-generator) and [Smart Coercion](/docs/plugins/smart-coercion). It accepts the same options as ArkType's `toJsonSchema`, see the [source code](https://github.com/middleapi/orpc/blob/main/packages/arktype/src/converter.ts) and ArkType's [JSON Schema configuration docs](https://arktype.io/docs/configuration#tojsonschema) for implementation details.
34+
35+
```ts
36+
import { OpenAPIGenerator } from '@orpc/openapi'
37+
import { ArkTypeToJsonSchemaConverter } from '@orpc/arktype'
38+
39+
const generator = new OpenAPIGenerator({
40+
converters: [new ArkTypeToJsonSchemaConverter()],
41+
})
42+
```
43+
44+
### Reusable Types
45+
46+
A common pattern is defining reusable or recursive types using scopes. The converter preserves them in `$defs`, which `OpenAPIGenerator` can then [hoist](/docs/openapi/specification#hoisting-defs) into `components.schemas`.
47+
48+
```ts
49+
import { scope } from 'arktype'
50+
51+
const types = scope({
52+
Planet: {
53+
name: 'string',
54+
neighbors: 'Planet[]',
55+
},
56+
})
57+
58+
const PlanetSchema = types.export().Planet
59+
```
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Standard Schema Integration
2+
3+
oRPC natively supports any library that implements the [Standard Schema](https://standardschema.dev/) specification, such as [Zod](/docs/integrations/zod), [Valibot](/docs/integrations/valibot), [ArkType](/docs/integrations/arktype), and [many more](https://standardschema.dev/schema#what-schema-libraries-implement-the-spec). Use them directly in `.input`, `.output`, and `.errors` without any extra setup.
4+
5+
```ts
6+
import * as z from 'zod'
7+
import * as v from 'valibot'
8+
9+
const example = os
10+
.input(z.object({ name: z.string() }))
11+
.output(v.object({ name: v.string() }))
12+
```
13+
14+
## Standard JSON Schema
15+
16+
[Standard JSON Schema](https://standardschema.dev/json-schema) is a companion specification that lets a schema library expose JSON Schema conversion in a standard way. Tools that rely on JSON Schema converters, such as the [OpenAPI Generator](/docs/openapi/specification#openapi-generator) and [Smart Coercion](/docs/plugins/smart-coercion), automatically fall back to Standard JSON Schema conversion when no configured converter matches a schema. So if your library also implements Standard JSON Schema, these tools work out of the box without a dedicated converter. Otherwise, the schema is treated as unknown and converted to an empty JSON schema.
17+
18+
### Building Your Own Converter
19+
20+
If your library does not implement Standard JSON Schema, or you want more control over the conversion, you can build your own converter by implementing the `JsonSchemaConverter` interface and passing it to the `converters` option. The first converter whose `condition` matches handles the schema:
21+
22+
```ts
23+
import type { AnySchema } from '@orpc/contract'
24+
import type {
25+
JsonSchema,
26+
JsonSchemaConverter,
27+
JsonSchemaConverterDirection
28+
} from '@orpc/json-schema'
29+
import { toJsonSchema } from '@valibot/to-json-schema'
30+
31+
class MyCustomConverter implements JsonSchemaConverter {
32+
condition(schema: AnySchema | undefined, _direction: JsonSchemaConverterDirection): boolean {
33+
return schema?.['~standard'].vendor === 'valibot'
34+
}
35+
36+
convert(
37+
schema: AnySchema | undefined,
38+
direction: JsonSchemaConverterDirection
39+
): [jsonSchema: JsonSchema, optional: boolean] {
40+
// In most cases, treating the schema as required is acceptable.
41+
return [toJsonSchema(schema as any), false] as any
42+
}
43+
}
44+
```
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Valibot Integration
2+
3+
[Valibot](https://valibot.dev/) implements [Standard Schema](/docs/integrations/standard-schema), so you can use it directly in your procedures without any extra setup. On top of that, `@orpc/valibot` provides a dedicated JSON Schema converter.
4+
5+
## Installation
6+
7+
::: code-group
8+
9+
```sh [npm]
10+
npm install @orpc/valibot@beta valibot
11+
```
12+
13+
```sh [yarn]
14+
yarn add @orpc/valibot@beta valibot
15+
```
16+
17+
```sh [pnpm]
18+
pnpm add @orpc/valibot@beta valibot
19+
```
20+
21+
```sh [bun]
22+
bun add @orpc/valibot@beta valibot
23+
```
24+
25+
```sh [deno]
26+
deno add npm:@orpc/valibot@beta npm:valibot
27+
```
28+
29+
:::
30+
31+
## JSON Schema Converter
32+
33+
`ValibotToJsonSchemaConverter` wraps [Valibot's built-in toJsonSchema](https://github.com/open-circle/valibot/blob/main/packages/to-json-schema/README.md) and adds support for additional types such as `v.bigint()`, `v.date()`, `v.set()`, and `v.map()`. Use it with tools such as the [OpenAPI Generator](/docs/openapi/specification#openapi-generator) and [Smart Coercion](/docs/plugins/smart-coercion). It accepts the same options as Valibot's `toJsonSchema`, see the [source code](https://github.com/middleapi/orpc/blob/main/packages/valibot/src/converter.ts) for implementation details.
34+
35+
```ts
36+
import { OpenAPIGenerator } from '@orpc/openapi'
37+
import { ValibotToJsonSchemaConverter } from '@orpc/valibot'
38+
39+
const generator = new OpenAPIGenerator({
40+
converters: [new ValibotToJsonSchemaConverter()],
41+
})
42+
```
43+
44+
### Reusable Schemas
45+
46+
A common pattern is defining reusable or recursive schemas via definitions. The converter preserves them in `$defs`, which `OpenAPIGenerator` can then [hoist](/docs/openapi/specification#hoisting-defs) into `components.schemas`. For more on how definitions work in Valibot, see [Valibot JSON Schema Definitions](https://github.com/open-circle/valibot/blob/main/packages/to-json-schema/README.md#definitions).
47+
48+
```ts
49+
import * as v from 'valibot'
50+
51+
const PlanetSchema = v.object({
52+
id: v.string(),
53+
name: v.string(),
54+
})
55+
56+
const generator = new OpenAPIGenerator({
57+
converters: [
58+
new ValibotToJsonSchemaConverter({
59+
definitions: { PlanetSchema },
60+
}),
61+
],
62+
})
63+
```
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Zod Integration
2+
3+
[Zod](https://zod.dev/) implements [Standard Schema](/docs/integrations/standard-schema), so you can use it directly in your procedures without any extra setup. On top of that, `@orpc/zod` provides a dedicated JSON Schema converter and registries for customizing the generated JSON schemas.
4+
5+
::: warning
6+
`@orpc/zod` requires Zod v4 or later.
7+
:::
8+
9+
## Installation
10+
11+
::: code-group
12+
13+
```sh [npm]
14+
npm install @orpc/zod@beta zod
15+
```
16+
17+
```sh [yarn]
18+
yarn add @orpc/zod@beta zod
19+
```
20+
21+
```sh [pnpm]
22+
pnpm add @orpc/zod@beta zod
23+
```
24+
25+
```sh [bun]
26+
bun add @orpc/zod@beta zod
27+
```
28+
29+
```sh [deno]
30+
deno add npm:@orpc/zod@beta npm:zod
31+
```
32+
33+
:::
34+
35+
## JSON Schema Converter
36+
37+
`ZodToJsonSchemaConverter` wraps [Zod's built-in toJSONSchema](https://zod.dev/json-schema?id=ztojsonschema#ztojsonschema) and adds support for additional types such as `z.bigint()`, `z.date()`, `z.set()`, and `z.map()`. Use it with tools such as the [OpenAPI Generator](/docs/openapi/specification#openapi-generator) and [Smart Coercion](/docs/plugins/smart-coercion). It accepts the same options as Zod's `toJSONSchema`, see the [source code](https://github.com/middleapi/orpc/blob/main/packages/zod/src/converter.ts) for implementation details.
38+
39+
```ts
40+
import { OpenAPIGenerator } from '@orpc/openapi'
41+
import { ZodToJsonSchemaConverter } from '@orpc/zod'
42+
43+
const generator = new OpenAPIGenerator({
44+
converters: [new ZodToJsonSchemaConverter()],
45+
})
46+
```
47+
48+
### Reusable Schemas
49+
50+
A common pattern is defining reusable schemas with `id` metadata. The converter places them in `$defs`, which `OpenAPIGenerator` then [hoists](/docs/openapi/specification#hoisting-defs) into `components.schemas`. For more on `id` and `$ref` in Zod, see [Zod JSON Schema Registries](https://zod.dev/json-schema?id=registries#registries).
51+
52+
```ts
53+
import * as z from 'zod'
54+
55+
const PlanetSchema = z.object({
56+
id: z.string(),
57+
name: z.string(),
58+
}).meta({ id: 'Planet' })
59+
```
60+
61+
### Customizing Generated JSON Schemas
62+
63+
`@orpc/zod` exposes registries for customizing the JSON schema generated for a given Zod schema. Registered entries are shallow merged over the generated JSON schema: `JSON_SCHEMA_REGISTRY` applies to both input and output, while `JSON_SCHEMA_INPUT_REGISTRY` and `JSON_SCHEMA_OUTPUT_REGISTRY` apply to a single direction and win on conflicting keys:
64+
65+
```ts
66+
import {
67+
JSON_SCHEMA_INPUT_REGISTRY,
68+
JSON_SCHEMA_OUTPUT_REGISTRY,
69+
JSON_SCHEMA_REGISTRY,
70+
} from '@orpc/zod'
71+
import * as z from 'zod'
72+
73+
const user = z.object({
74+
name: z.string(),
75+
age: z.string().transform(v => Number(v)),
76+
})
77+
78+
JSON_SCHEMA_REGISTRY.add(user, {
79+
description: 'A user',
80+
})
81+
82+
JSON_SCHEMA_INPUT_REGISTRY.add(user, {
83+
examples: [{ name: 'John', age: '20' }],
84+
})
85+
86+
JSON_SCHEMA_OUTPUT_REGISTRY.add(user, {
87+
examples: [{ name: 'John', age: 20 }],
88+
})
89+
```

0 commit comments

Comments
 (0)