Skip to content

Commit 98e1b3d

Browse files
authored
feat(valibot): ValibotToJsonSchemaConverter (#261)
closes: https://github.com/unnoq/orpc/issues/162 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Expanded API specification generation to support an additional schema library option, allowing users to choose between multiple schema integrations. - Streamlined setup by removing an outdated dependency from installation commands. - Introduced experimental warnings for emerging features. - **Documentation** - Updated guides and installation instructions to reflect broader schema support. - Added comprehensive documentation outlining the new integration for enhanced developer experience. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 283e153 commit 98e1b3d

File tree

10 files changed

+307
-8
lines changed

10 files changed

+307
-8
lines changed

apps/content/docs/openapi/openapi-specification.md

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,43 +12,47 @@ oRPC uses the [OpenAPI Specification](https://spec.openapis.org/oas/v3.1.0) to d
1212
::: code-group
1313

1414
```sh [npm]
15-
npm install @orpc/openapi@latest @orpc/zod@latest
15+
npm install @orpc/openapi@latest
1616
```
1717

1818
```sh [yarn]
19-
yarn add @orpc/openapi@latest @orpc/zod@latest
19+
yarn add @orpc/openapi@latest
2020
```
2121

2222
```sh [pnpm]
23-
pnpm add @orpc/openapi@latest @orpc/zod@latest
23+
pnpm add @orpc/openapi@latest
2424
```
2525

2626
```sh [bun]
27-
bun add @orpc/openapi@latest @orpc/zod@latest
27+
bun add @orpc/openapi@latest
2828
```
2929

3030
```sh [deno]
31-
deno install npm:@orpc/openapi@latest npm:@orpc/zod@latest
31+
deno install npm:@orpc/openapi@latest
3232
```
3333

3434
:::
3535

3636
## Generating Specifications
3737

38-
oRPC supports only OpenAPI 3.1.1 and uses [Zod](https://github.com/colinhacks/zod) as its sole schema library. You can generate specifications from either a [Router](/docs/router) or a [Contract](/docs/contract-first/define-contract):
38+
oRPC supports OpenAPI 3.1.1 and integrates seamlessly with popular schema libraries like [Zod](https://github.com/colinhacks/zod), and [Valibot](https://valibot.dev). You can generate specifications from either a [Router](/docs/router) or a [Contract](/docs/contract-first/define-contract):
3939

4040
:::info
41-
Interested in [Valibot](https://valibot.dev) support? Help us prioritize this feature by casting your vote on [Github](https://github.com/unnoq/orpc/issues/162)
41+
Interested in support for additional schema libraries? [Let us know](https://github.com/unnoq/orpc/discussions/categories/ideas)!
4242
:::
4343

4444
```ts twoslash
4545
import { contract, router } from './shared/planet'
4646
// ---cut---
4747
import { OpenAPIGenerator } from '@orpc/openapi'
4848
import { ZodToJsonSchemaConverter } from '@orpc/zod'
49+
import { experimental_ValibotToJsonSchemaConverter as ValibotToJsonSchemaConverter } from '@orpc/valibot'
4950

5051
const openAPIGenerator = new OpenAPIGenerator({
51-
schemaConverters: [new ZodToJsonSchemaConverter()],
52+
schemaConverters: [
53+
new ZodToJsonSchemaConverter(), // <-- if you use Zod
54+
new ValibotToJsonSchemaConverter(), // <-- if you use Valibot
55+
],
5256
})
5357

5458
const specFromContract = await openAPIGenerator.generate(contract, {
@@ -66,6 +70,10 @@ const specFromRouter = await openAPIGenerator.generate(router, {
6670
})
6771
```
6872

73+
:::warning
74+
Features prefixed with `experimental_` are unstable and may lack some functionality.
75+
:::
76+
6977
## Operation Metadata
7078

7179
You can enrich your API documentation by specifying operation metadata using the `.route` or `.tag`:

apps/content/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"@orpc/contract": "workspace:*",
1313
"@orpc/react-query": "workspace:*",
1414
"@orpc/server": "workspace:*",
15+
"@orpc/valibot": "workspace:*",
1516
"@orpc/vue-colada": "workspace:*",
1617
"@orpc/vue-query": "workspace:*",
1718
"@orpc/zod": "workspace:*",

packages/valibot/.gitignore

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Hidden folders and files
2+
.*
3+
!.gitignore
4+
!.*.example
5+
6+
# Common generated folders
7+
logs/
8+
node_modules/
9+
out/
10+
dist/
11+
dist-ssr/
12+
build/
13+
coverage/
14+
temp/
15+
16+
# Common generated files
17+
*.log
18+
*.log.*
19+
*.tsbuildinfo
20+
*.vitest-temp.json
21+
vite.config.ts.timestamp-*
22+
vitest.config.ts.timestamp-*
23+
24+
# Common manual ignore files
25+
*.local
26+
*.pem

packages/valibot/README.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<div align="center">
2+
<image align="center" src="https://orpc.unnoq.com/logo.webp" width=280 alt="oRPC logo" />
3+
</div>
4+
5+
<h1></h1>
6+
7+
<div align="center">
8+
<a href="https://codecov.io/gh/unnoq/orpc">
9+
<img alt="codecov" src="https://codecov.io/gh/unnoq/orpc/branch/main/graph/badge.svg">
10+
</a>
11+
<a href="https://www.npmjs.com/package/@orpc/valibot">
12+
<img alt="weekly downloads" src="https://img.shields.io/npm/dw/%40orpc%2Fvalibot?logo=npm" />
13+
</a>
14+
<a href="https://github.com/unnoq/orpc/blob/main/LICENSE">
15+
<img alt="MIT License" src="https://img.shields.io/github/license/unnoq/orpc?logo=open-source-initiative" />
16+
</a>
17+
<a href="https://discord.gg/TXEbwRBvQn">
18+
<img alt="Discord" src="https://img.shields.io/discord/1308966753044398161?color=7389D8&label&logo=discord&logoColor=ffffff" />
19+
</a>
20+
</div>
21+
22+
<h3 align="center">Typesafe APIs Made Simple 🪄</h3>
23+
24+
**oRPC is a powerful combination of RPC and OpenAPI**, makes it easy to build APIs that are end-to-end type-safe and adhere to OpenAPI standards, ensuring a smooth and enjoyable developer experience.
25+
26+
---
27+
28+
## Highlights
29+
30+
- **End-to-End Type Safety 🔒**: Ensure complete type safety from inputs to outputs and errors, bridging server and client seamlessly.
31+
- **First-Class OpenAPI 📄**: Adheres to the OpenAPI standard out of the box, ensuring seamless integration and comprehensive API documentation.
32+
- **Contract-First Development 📜**: (Optional) Define your API contract upfront and implement it with confidence.
33+
- **Exceptional Developer Experience ✨**: Enjoy a streamlined workflow with robust typing and clear, in-code documentation.
34+
- **Multi-Runtime Support 🌍**: Run your code seamlessly on Cloudflare, Deno, Bun, Node.js, and more.
35+
- **Framework Integrations 🧩**: Supports Tanstack Query (React, Vue, Solid, Svelte), Pinia Colada, and more.
36+
- **Server Actions ⚡️**: Fully compatible with React Server Actions on Next.js, TanStack Start, and more.
37+
- **Standard Schema Support 🗂️**: Effortlessly work with Zod, Valibot, ArkType, and others right out of the box.
38+
- **Fast & Lightweight 💨**: Built on native APIs across all runtimes – optimized for speed and efficiency.
39+
- **Native Types 📦**: Enjoy built-in support for Date, File, Blob, BigInt, URL and more with no extra setup.
40+
- **Lazy Router ⏱️**: Improve cold start times with our lazy routing feature.
41+
- **SSE & Streaming 📡**: Provides SSE and streaming features – perfect for real-time notifications and AI-powered streaming responses.
42+
- **Reusability 🔄**: Write once and reuse your code across multiple purposes effortlessly.
43+
- **Extendability 🔌**: Easily enhance oRPC with plugins, middleware, and interceptors.
44+
- **Reliability 🛡️**: Well-tested, fully TypeScript, production-ready, and MIT licensed for peace of mind.
45+
- **Simplicity 💡**: Enjoy straightforward, clean code with no hidden magic.
46+
47+
## Documentation
48+
49+
You can find the full documentation [here](https://orpc.unnoq.com).
50+
51+
## Packages
52+
53+
- [@orpc/contract](https://www.npmjs.com/package/@orpc/contract): Build your API contract.
54+
- [@orpc/server](https://www.npmjs.com/package/@orpc/server): Build your API or implement API contract.
55+
- [@orpc/client](https://www.npmjs.com/package/@orpc/client): Consume your API on the client with type-safety.
56+
- [@orpc/react-query](https://www.npmjs.com/package/@orpc/react-query): Integration with [React Query](https://tanstack.com/query/latest/docs/framework/react/overview).
57+
- [@orpc/vue-query](https://www.npmjs.com/package/@orpc/vue-query): Integration with [Vue Query](https://tanstack.com/query/latest/docs/framework/vue/overview).
58+
- [@orpc/solid-query](https://www.npmjs.com/package/@orpc/solid-query): Integration with [Solid Query](https://tanstack.com/query/latest/docs/framework/solid/overview).
59+
- [@orpc/svelte-query](https://www.npmjs.com/package/@orpc/svelte-query): Integration with [Svelte Query](https://tanstack.com/query/latest/docs/framework/svelte/overview).
60+
- [@orpc/vue-colada](https://www.npmjs.com/package/@orpc/vue-colada): Integration with [Pinia Colada](https://pinia-colada.esm.dev/).
61+
- [@orpc/openapi](https://www.npmjs.com/package/@orpc/openapi): Generate OpenAPI specs and handle OpenAPI requests.
62+
- [@orpc/zod](https://www.npmjs.com/package/@orpc/zod): More schemas that [Zod](https://zod.dev/) doesn't support yet.
63+
- [@orpc/valibot](https://www.npmjs.com/package/@orpc/zod): OpenAPI spec generation from [Valibot](https://valibot.dev/).
64+
65+
## `@orpc/valibot`
66+
67+
Provides `ValibotToJsonSchemaConverter` for generating OpenAPI specs from [Valibot](https://valibot.dev/).
68+
69+
### Generate OpenAPI Spec
70+
71+
```ts
72+
import { OpenAPIGenerator } from '@orpc/openapi'
73+
import { experimental_ValibotToJsonSchemaConverter as ValibotToJsonSchemaConverter } from '@orpc/valibot'
74+
75+
const openAPIGenerator = new OpenAPIGenerator({
76+
schemaConverters: [
77+
new ValibotToJsonSchemaConverter()
78+
],
79+
})
80+
81+
const specFromContract = await openAPIGenerator.generate(contract, {
82+
info: {
83+
title: 'My App',
84+
version: '0.0.0',
85+
},
86+
})
87+
88+
const specFromRouter = await openAPIGenerator.generate(router, {
89+
info: {
90+
title: 'My App',
91+
version: '0.0.0',
92+
},
93+
})
94+
```
95+
96+
## License
97+
98+
Distributed under the MIT License. See [LICENSE](https://github.com/unnoq/orpc/blob/main/LICENSE) for more information.

packages/valibot/package.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"name": "@orpc/valibot",
3+
"type": "module",
4+
"version": "0.0.0",
5+
"license": "MIT",
6+
"homepage": "https://orpc.unnoq.com",
7+
"repository": {
8+
"type": "git",
9+
"url": "git+https://github.com/unnoq/orpc.git",
10+
"directory": "packages/valibot"
11+
},
12+
"keywords": [
13+
"unnoq",
14+
"orpc"
15+
],
16+
"publishConfig": {
17+
"exports": {
18+
".": {
19+
"types": "./dist/index.d.mts",
20+
"import": "./dist/index.mjs",
21+
"default": "./dist/index.mjs"
22+
}
23+
}
24+
},
25+
"exports": {
26+
".": "./src/index.ts"
27+
},
28+
"files": [
29+
"dist"
30+
],
31+
"scripts": {
32+
"build": "unbuild",
33+
"build:watch": "pnpm run build --watch",
34+
"type:check": "tsc -b"
35+
},
36+
"peerDependencies": {
37+
"@orpc/contract": "workspace:*",
38+
"@orpc/server": "workspace:*",
39+
"valibot": "*"
40+
},
41+
"dependencies": {
42+
"@orpc/openapi": "workspace:*",
43+
"@orpc/shared": "workspace:*",
44+
"@valibot/to-json-schema": "1.0.0-rc.0"
45+
}
46+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import * as v from 'valibot'
2+
import { z } from 'zod'
3+
import { experimental_ValibotToJsonSchemaConverter as ValibotToJsonSchemaConverter } from './converter'
4+
5+
it('valibotToJsonSchemaConverter.convert', async () => {
6+
const converter = new ValibotToJsonSchemaConverter()
7+
expect(converter.convert(v.string(), { strategy: 'input' })).toEqual(
8+
[true, {
9+
$schema: 'http://json-schema.org/draft-07/schema#',
10+
type: 'string',
11+
}],
12+
)
13+
expect(converter.convert(v.object({ a: v.string() }), { strategy: 'input' })).toEqual(
14+
[true, {
15+
$schema: 'http://json-schema.org/draft-07/schema#',
16+
type: 'object',
17+
properties: { a: { type: 'string' } },
18+
required: ['a'],
19+
}],
20+
)
21+
})
22+
23+
it('valibotToJsonSchemaConverter.condition', async () => {
24+
const converter = new ValibotToJsonSchemaConverter()
25+
expect(converter.condition(v.string())).toBe(true)
26+
expect(converter.condition(v.optional(v.string()))).toBe(true)
27+
expect(converter.condition(z.string())).toBe(false)
28+
expect(converter.condition(z.string().optional())).toBe(false)
29+
})

packages/valibot/src/converter.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import type { AnySchema } from '@orpc/contract'
2+
import type { ConditionalSchemaConverter, JSONSchema, SchemaConvertOptions } from '@orpc/openapi'
3+
import { type ConversionConfig, toJsonSchema } from '@valibot/to-json-schema'
4+
5+
export interface experimental_ValibotToJsonSchemaConverterOptions extends Pick<ConversionConfig, 'errorMode'> {
6+
7+
}
8+
9+
export class experimental_ValibotToJsonSchemaConverter implements ConditionalSchemaConverter {
10+
private readonly errorMode: experimental_ValibotToJsonSchemaConverterOptions['errorMode']
11+
12+
constructor(options: experimental_ValibotToJsonSchemaConverterOptions = {}) {
13+
this.errorMode = options.errorMode ?? 'ignore'
14+
}
15+
16+
condition(schema: AnySchema | undefined): boolean {
17+
return schema !== undefined && schema['~standard'].vendor === 'valibot'
18+
}
19+
20+
convert(schema: AnySchema | undefined, _options: SchemaConvertOptions): [required: boolean, jsonSchema: Exclude<JSONSchema, boolean>] {
21+
const jsonSchema = toJsonSchema(schema as any, { errorMode: this.errorMode })
22+
23+
return [true, jsonSchema as any]
24+
}
25+
}

packages/valibot/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './converter'

packages/valibot/tsconfig.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"extends": "../../tsconfig.lib.json",
3+
"compilerOptions": {
4+
"rootDir": "src"
5+
},
6+
"references": [
7+
{ "path": "../openapi" },
8+
{ "path": "../contract" },
9+
{ "path": "../server" },
10+
{ "path": "../shared" }
11+
],
12+
"include": ["src"],
13+
"exclude": [
14+
"**/*.test.*",
15+
"**/*.test-d.ts",
16+
"**/__tests__/**",
17+
"**/__mocks__/**",
18+
"**/__snapshots__/**"
19+
]
20+
}

0 commit comments

Comments
 (0)