Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

unnoq/orpc

Typesafe APIs Made Simple πŸͺ„

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.
  • πŸ” First-Class OpenTelemetry: Seamlessly integrate with OpenTelemetry for observability.
  • βš™οΈ Framework Integrations: Seamlessly integrate with TanStack Query (React, Vue, Solid, Svelte, Angular), SWR, 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.

Documentation

You can find the full documentation here.

Packages

Overview

This is a quick overview of how to use oRPC. For more details, please refer to the documentation.

  1. Define your router:

    import type { IncomingHttpHeaders } from 'node:http'
    import { ORPCError, os } from '@orpc/server'
    import * as z from 'zod'
    const PlanetSchema = z.object({
     id: z.number().int().min(1),
     name: z.string(),
     description: z.string().optional(),
    })
    export const listPlanet = os
     .input(
     z.object({
     limit: z.number().int().min(1).max(100).optional(),
     cursor: z.number().int().min(0).default(0),
     }),
     )
     .handler(async ({ input }) => {
     // your list code here
     return [{ id: 1, name: 'name' }]
     })
    export const findPlanet = os
     .input(PlanetSchema.pick({ id: true }))
     .handler(async ({ input }) => {
     // your find code here
     return { id: 1, name: 'name' }
     })
    export const createPlanet = os
     .$context<{ headers: IncomingHttpHeaders }>()
     .use(({ context, next }) => {
     const user = parseJWT(context.headers.authorization?.split(' ')[1])
     if (user) {
     return next({ context: { user } })
     }
     throw new ORPCError('UNAUTHORIZED')
     })
     .input(PlanetSchema.omit({ id: true }))
     .handler(async ({ input, context }) => {
     // your create code here
     return { id: 1, name: 'name' }
     })
    export const router = {
     planet: {
     list: listPlanet,
     find: findPlanet,
     create: createPlanet
     }
    }
  2. Create your server:

    import { createServer } from 'node:http'
    import { RPCHandler } from '@orpc/server/node'
    import { CORSPlugin } from '@orpc/server/plugins'
    const handler = new RPCHandler(router, {
     plugins: [new CORSPlugin()]
    })
    const server = createServer(async (req, res) => {
     const result = await handler.handle(req, res, {
     context: { headers: req.headers }
     })
     if (!result.matched) {
     res.statusCode = 404
     res.end('No procedure matched')
     }
    })
    server.listen(
     3000,
     '127.0.0.1',
     () => console.log('Listening on 127.0.0.1:3000')
    )
  3. Create your client:

    import type { RouterClient } from '@orpc/server'
    import { createORPCClient } from '@orpc/client'
    import { RPCLink } from '@orpc/client/fetch'
    const link = new RPCLink({
     url: 'http://127.0.0.1:3000',
     headers: { Authorization: 'Bearer token' },
    })
    export const orpc: RouterClient<typeof router> = createORPCClient(link)
  4. Consume your API:

    import { orpc } from './client'
    const planets = await orpc.planet.list({ limit: 10 })
  5. Generate OpenAPI Spec:

    import { OpenAPIGenerator } from '@orpc/openapi'
    import { ZodToJsonSchemaConverter } from '@orpc/zod/zod4'
    const generator = new OpenAPIGenerator({
     schemaConverters: [new ZodToJsonSchemaConverter()]
    })
    const spec = await generator.generate(router, {
     info: {
     title: 'Planet API',
     version: '1.0.0'
     }
    })
    console.log(spec)

Sponsors

References

oRPC is inspired by existing solutions that prioritize type safety and developer experience. Special acknowledgments to:

  • tRPC: For pioneering the concept of end-to-end type-safe RPC and influencing the development of type-safe APIs.
  • ts-rest: For its emphasis on contract-first development and OpenAPI integration, which have greatly inspired oRPC's feature set.

License

Distributed under the MIT License. See LICENSE for more information.

Sponsor this project

Packages

No packages published

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /