Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/content/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ export default defineConfig({
{ text: 'Durable Event Iterator', link: '/docs/integrations/durable-event-iterator' },
{ text: 'Hey API', link: '/docs/integrations/hey-api' },
{ text: 'NestJS', link: '/docs/openapi/integrations/implement-contract-in-nest' },
{ text: 'tRPC', link: '/docs/openapi/integrations/trpc' },
],
},
{
Expand Down Expand Up @@ -221,6 +222,7 @@ export default defineConfig({
collapsed: true,
items: [
{ text: 'Implement Contract in NestJS', link: '/docs/openapi/integrations/implement-contract-in-nest' },
{ text: 'tRPC', link: '/docs/openapi/integrations/trpc' },
],
},
{
Expand Down
124 changes: 124 additions & 0 deletions apps/content/docs/openapi/integrations/trpc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
---
title: tRPC Integration
description: Use oRPC features in your tRPC applications.
---

# tRPC Integration

This guide explains how to integrate oRPC with tRPC, allowing you to leverage oRPC features in your existing tRPC applications.

## Installation

::: code-group

```sh [npm]
npm install @orpc/trpc@latest
```

```sh [yarn]
yarn add @orpc/trpc@latest
```

```sh [pnpm]
pnpm add @orpc/trpc@latest
```

```sh [bun]
bun add @orpc/trpc@latest
```

```sh [deno]
deno install npm:@orpc/trpc@latest
```

:::

## OpenAPI Support

By converting a [tRPC router](https://trpc.io/docs/server/routers) to an [oRPC router](/docs/router), you can utilize most oRPC features, including OpenAPI specification generation and request handling.

```ts
import {
experimental_ORPCMeta as ORPCMeta,
experimental_toORPCRouter as toORPCRouter
} from '@orpc/trpc'

export const t = initTRPC.context<Context>().meta<ORPCMeta>().create()

const orpcRouter = toORPCRouter(trpcRouter)
```

::: warning
Ensure you set the `.meta` type to `ORPCMeta` when creating your tRPC builder. This is required for OpenAPI features to function properly.
:::

### Specification Generation

```ts
const openAPIGenerator = new OpenAPIGenerator({
schemaConverters: [
new ZodToJsonSchemaConverter(), // <-- if you use Zod
new ValibotToJsonSchemaConverter(), // <-- if you use Valibot
new ArkTypeToJsonSchemaConverter(), // <-- if you use ArkType
],
})

const spec = await openAPIGenerator.generate(orpcRouter, {
info: {
title: 'My App',
version: '0.0.0',
},
})
```

::: info
Learn more about [oRPC OpenAPI Specification Generation](/docs/openapi/openapi-specification).
:::

### Request Handling

```ts
const handler = new OpenAPIHandler(orpcRouter, {
plugins: [new CORSPlugin()],
interceptors: [
onError(error => console.error(error))
],
})

export async function fetch(request: Request) {
const { matched, response } = await handler.handle(request, {
prefix: '/api',
context: {} // Add initial context if needed
})

return response ?? new Response('Not Found', { status: 404 })
}
```

::: info
Learn more about [oRPC OpenAPI Handler](/docs/openapi/openapi-handler).
:::

## Error Formatting

The `toORPCRouter` does not support [tRPC Error Formatting](https://trpc.io/docs/server/error-formatting). You should catch errors and format them manually using interceptors:

```ts
const handler = new OpenAPIHandler(orpcRouter, {
interceptors: [
onError((error) => {
if (
error instanceof ORPCError
&& error.cause instanceof TRPCError
&& error.cause.cause instanceof ZodError
) {
throw new ORPCError('INPUT_VALIDATION_FAILED', {
status: 422,
data: error.cause.cause.flatten(),
cause: error.cause.cause,
})
}
})
],
})
```
26 changes: 26 additions & 0 deletions packages/trpc/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Hidden folders and files
.*
!.gitignore
!.*.example

# Common generated folders
logs/
node_modules/
out/
dist/
dist-ssr/
build/
coverage/
temp/

# Common generated files
*.log
*.log.*
*.tsbuildinfo
*.vitest-temp.json
vite.config.ts.timestamp-*
vitest.config.ts.timestamp-*

# Common manual ignore files
*.local
*.pem
76 changes: 76 additions & 0 deletions packages/trpc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<div align="center">
<image align="center" src="https://orpc.unnoq.com/logo.webp" width=280 alt="oRPC logo" />
</div>

<h1></h1>

<div align="center">
<a href="https://codecov.io/gh/unnoq/orpc">
<img alt="codecov" src="https://codecov.io/gh/unnoq/orpc/branch/main/graph/badge.svg">
</a>
<a href="https://www.npmjs.com/package/@orpc/trpc">
<img alt="weekly downloads" src="https://img.shields.io/npm/dw/%40orpc%2Ftrpc?logo=npm" />
</a>
<a href="https://github.com/unnoq/orpc/blob/main/LICENSE">
<img alt="MIT License" src="https://img.shields.io/github/license/unnoq/orpc?logo=open-source-initiative" />
</a>
<a href="https://discord.gg/TXEbwRBvQn">
<img alt="Discord" src="https://img.shields.io/discord/1308966753044398161?color=7389D8&label&logo=discord&logoColor=ffffff" />
</a>
</div>

<h3 align="center">Typesafe APIs Made Simple 🪄</h3>

**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

---

## Highlights

- **🔗 End-to-End Type Safety**: Ensure type-safe inputs, outputs, and errors from client to server.
- **📘 First-Class OpenAPI**: Built-in support that fully adheres to the OpenAPI standard.
- **📝 Contract-First Development**: Optionally define your API contract before implementation.
- **⚙️ Framework Integrations**: Seamlessly integrate with TanStack Query (React, Vue, Solid, Svelte, Angular), Pinia Colada, and more.
- **🚀 Server Actions**: Fully compatible with React Server Actions on Next.js, TanStack Start, and other platforms.
- **🔠 Standard Schema Support**: Works out of the box with Zod, Valibot, ArkType, and other schema validators.
- **🗃️ Native Types**: Supports native types like Date, File, Blob, BigInt, URL, and more.
- **⏱️ Lazy Router**: Enhance cold start times with our lazy routing feature.
- **📡 SSE & Streaming**: Enjoy full type-safe support for SSE and streaming.
- **🌍 Multi-Runtime Support**: Fast and lightweight on Cloudflare, Deno, Bun, Node.js, and beyond.
- **🔌 Extendability**: Easily extend functionality with plugins, middleware, and interceptors.
- **🛡️ Reliability**: Well-tested, TypeScript-based, production-ready, and MIT licensed.

## Documentation

You can find the full documentation [here](https://orpc.unnoq.com).

## Packages

- [@orpc/contract](https://www.npmjs.com/package/@orpc/contract): Build your API contract.
- [@orpc/server](https://www.npmjs.com/package/@orpc/server): Build your API or implement API contract.
- [@orpc/client](https://www.npmjs.com/package/@orpc/client): Consume your API on the client with type-safety.
- [@orpc/openapi](https://www.npmjs.com/package/@orpc/openapi): Generate OpenAPI specs and handle OpenAPI requests.
- [@orpc/nest](https://www.npmjs.com/package/@orpc/nest): Deeply integrate oRPC with [NestJS](https://nestjs.com/).
- [@orpc/react](https://www.npmjs.com/package/@orpc/react): Utilities for integrating oRPC with React and React Server Actions.
- [@orpc/tanstack-query](https://www.npmjs.com/package/@orpc/tanstack-query): [TanStack Query](https://tanstack.com/query/latest) integration.
- [@orpc/vue-colada](https://www.npmjs.com/package/@orpc/vue-colada): Integration with [Pinia Colada](https://pinia-colada.esm.dev/).
- [@orpc/hey-api](https://www.npmjs.com/package/@orpc/hey-api): [Hey API](https://heyapi.dev/) integration.
- [@orpc/zod](https://www.npmjs.com/package/@orpc/zod): More schemas that [Zod](https://zod.dev/) doesn't support yet.
- [@orpc/valibot](https://www.npmjs.com/package/@orpc/valibot): OpenAPI spec generation from [Valibot](https://valibot.dev/).
- [@orpc/arktype](https://www.npmjs.com/package/@orpc/arktype): OpenAPI spec generation from [ArkType](https://arktype.io/).

## `@orpc/trpc`

Bridge between [tRPC](https://trpc.io/) and oRPC

## Sponsors

<p align="center">
<a href="https://cdn.jsdelivr.net/gh/unnoq/unnoq/sponsors.svg">
<img src='https://cdn.jsdelivr.net/gh/unnoq/unnoq/sponsors.svg'/>
</a>
</p>

## License

Distributed under the MIT License. See [LICENSE](https://github.com/unnoq/orpc/blob/main/LICENSE) for more information.
48 changes: 48 additions & 0 deletions packages/trpc/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "@orpc/trpc",
"type": "module",
"version": "0.0.0",
"license": "MIT",
"homepage": "https://orpc.unnoq.com",
"repository": {
"type": "git",
"url": "git+https://github.com/unnoq/orpc.git",
"directory": "packages/trpc"
},
"keywords": [
"unnoq",
"orpc",
"trpc"
],
"publishConfig": {
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs",
"default": "./dist/index.mjs"
}
}
},
"exports": {
".": "./src/index.ts"
},
"files": [
"dist"
],
"scripts": {
"build": "unbuild",
"build:watch": "pnpm run build --watch",
"type:check": "tsc -b"
},
"peerDependencies": {
"@trpc/server": ">=11.4.2"
},
"dependencies": {
"@orpc/server": "workspace:*",
"@orpc/shared": "workspace:*"
},
"devDependencies": {
"@trpc/server": "^11.4.2",
"zod": "^3.25.67"
}
}
Empty file added packages/trpc/src/index.ts
Empty file.
44 changes: 44 additions & 0 deletions packages/trpc/src/to-orpc-router.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { ContractRouter, InferRouterInitialContext, Procedure, Router, Schema } from '@orpc/server'
import type { inferRouterContext } from '@trpc/server'
import type { inferRouterMeta } from '@trpc/server/unstable-core-do-not-import'
import type { TRPCContext, TRPCMeta, trpcRouter } from '../tests/shared'
import type { experimental_ToORPCRouterResult as ToORPCRouterResult } from './to-orpc-router'

it('ToORPCRouterResult', () => {
const orpcRouter = {} as ToORPCRouterResult<
inferRouterContext<typeof trpcRouter>,
inferRouterMeta<typeof trpcRouter>,
typeof trpcRouter['_def']['record']
>

expectTypeOf(orpcRouter).toExtend<Router<ContractRouter<TRPCMeta>, TRPCContext>>()

expectTypeOf<InferRouterInitialContext<typeof orpcRouter>>().toEqualTypeOf<{ a: string }>()

expectTypeOf(orpcRouter.ping).toEqualTypeOf<
Procedure<TRPCContext, object, Schema<{ input: number }, unknown>, Schema<unknown, { output: string }>, object, TRPCMeta>
>()

expectTypeOf(orpcRouter.throw).toEqualTypeOf<
Procedure<TRPCContext, object, Schema<{ b: number, c: string }, unknown>, Schema<unknown, never>, object, TRPCMeta>
>()

expectTypeOf(orpcRouter.subscribe).toEqualTypeOf<
Procedure<TRPCContext, object, Schema<{ u: string }, unknown>, Schema<unknown, AsyncIterable<string, void, any>>, object, TRPCMeta>
>()

expectTypeOf(orpcRouter.nested).toEqualTypeOf<
{
ping: Procedure<TRPCContext, object, Schema<{ a: string }, unknown>, Schema<unknown, number>, object, TRPCMeta>
}
>()

expectTypeOf(orpcRouter.lazy).toEqualTypeOf<
{
subscribe: Procedure<TRPCContext, object, Schema<void, unknown>, Schema<unknown, AsyncIterable<string, void, any>>, object, TRPCMeta>
lazy: {
throw: Procedure<TRPCContext, object, Schema<{ input: number }, unknown>, Schema<unknown, { output: string }>, object, TRPCMeta>
}
}
>()
})
Loading