background

End-to-End Typesafe API Builder for Developers

oRPC is an open-source toolkit that helps developers build robust TypeScript functions and expose them to the internet using typesafe clients, OpenAPI standards, or even server actions. Our focus is on developer experience, performance, and reliability.

import type { ,  } from '@orpc/server'
import { ,  } from '@orpc/server'
import {  } from '@orpc/zod'
import {  } from 'zod'
 
export type  = { ?: { : string } }
 
// global pub, authed completely optional
export const  /** public access */ = .<>()
export const  /** require authed */ = .((, , ) => /** put auth logic here */ .({}))
 
export const  = .({
  : 
    .(
      .({
        : .(),
      }),
    )
    .(async (, , ) => {
      return {
        : `Hello, ${.}!`,
      }
    }),
 
  : .('/posts').({
    : 
      .({
        : '/{id}', // custom your OpenAPI
        : 'GET',
      })
      .(
        .({
          : .({}),
        }),
      )
      .(
        .({
          : .(),
          : .(),
          : .(),
        }),
      )
      .(async (, , ) => {
        if (!.) {
          throw new ({
            : 'UNAUTHORIZED',
          })
        }
 
        const  = await .({
          : {
            : ., // from now user not undefined-able
          },
        })
 
        const  = . // do something on success
 
        return 
      })
      .((, , ) => {
        return {
          : 'example',
          : 'example',
          : 'example',
        }
      }),
 
    : 
      .(
        .({
          : .(),
          : .(),
          : .().('image/*'),
        }),
      )
      .(async (, , ) => {
        const  = . // file upload out of the box
 
        return {
          : 'example',
          : .,
          : .,
        }
      }),
  }),
})
 
export type  = <typeof >
export type  = <typeof >
 
// Expose apis to the internet with fetch handler
import {  } from '@orpc/openapi/fetch'
import { ,  } from '@orpc/server/fetch'
// Modern runtime that support fetch api like deno, bun, cloudflare workers, even node can used
import {  } from 'node:http'
import {  } from '@whatwg-node/server'
 
const  = (
  ((: Request) => {
    const  = new (.)
 
    if (..('/api')) {
      return ({
        ,
        ,
        : '/api',
        : {},
        : [
          (),
          (),
        ],
      })
    }
 
    return new ('Not found', { : 404 })
  }),
)
 
.(3000, () => {
  // eslint-disable-next-line no-console
  .('Server is available at http://localhost:3000')
})
 
//
//
 
export default 
import { ,  } from '@orpc/client'
import type {  } from 'examples/server'
 
const  = <typeof  /* or contract router */>({
  : 'http://localhost:3000/api',
  // fetch: optional override for the default fetch function
  // headers: provide additional headers
})
 
//  File upload out of the box
const  = await ..({
  : 'My Amazing Title',
  : 'This is a detailed description of my content',
  : (.('thumb') as HTMLInputElement).[0]!,
})
 
..
  • create
  • find
// typesafe and completion out of box try { const = await ..({ : 'example' }) } catch () { if ( instanceof ) { // handle error } }

Build Robust, Typesafe Functions

export const getting = os
	.use(authMiddleware) // require auth
	.use(cache('5m')) // cache the output
	.use(canMiddleware, (i) => i.id) // permission check by id
	.route({
		path: '/getting/{id}' // dynamic params support
		method: 'POST' // custom OpenAPI method
	})
	.input(z.object({
		id: z.bigint(),
		user: z.object({
			name: z.string(),
			avatar: oz.file().type('image/*')
		})
	}))
	.output(z.string()) // validate output
	.func(async (input) => 'Name and Avatar has been updated')

Only the .func method is required. All other chain methods are optional.

With Middleware and the Procedure Builder, you can create reusable logic that ensures type safety and adds power and flexibility to your functions.

Use as a Regular Function

const text = await getting({ 
	id: 1992n,
	user: {
		name: 'unnoq',
		avatar: await readFile('/image.jpg'),
	}
})

The Procedure Caller feature lets your procedures behave like regular TypeScript functions.

Expose It Online with a Fully Typed Client

const text = await orpc.getting({ 
	id: 1992n,
	user: {
		name: 'unnoq',
		avatar: document.getElementById('avatar').files[0],
	}
})

Our Vanilla Client is fully typed and doesn't rely on generated code—thanks to TypeScript!

Seamless Integration with TanStack Query

// Fetch data with oRPC
const { data, status } = useQuery(
  orpc.post.find.queryOptions({ input: { id: 'example' } })
);
 
// Perform a mutation and invalidate related queries on success
const { mutate, isPending } = useMutation(
  orpc.getting.mutationOptions({
    onSuccess() {
      queryClient.invalidateQueries({
        queryKey: orpc.post.find.key({ input: { id: 'example' } }),
      });
    },
  })
);
 
// Execute mutation with structured input
mutate({
  id: 1992n,
  user: {
    name: 'unnoq',
    avatar: document.getElementById('avatar').files[0], // Handle file uploads
  },
});

We now support React Query Integration and Vue Query Integration.

Access via OpenAPI Standard

curl -X POST http://localhost:2026/api/getting/1992 \
  -H "Content-Type: multipart/form-data" \
  -F "user[name]=unnoq" \
  -F "user[avatar]=@/path/to/your/image.jpg"

Features like Smart Conversion and Bracket Notation automatically convert 1992 into a bigint and seamlessly parse objects like user.

Use as a Server Action

// call directly from client
const onSubmit = () => { getting({ /***/ }) }
// use with a hook
const { execute, isPending, isError, error, output, input, status } = useAction(getting)
// createSafeAction and useSafeAction if you want catch error on client
// use as a form action
const gettingFA = createFormAction({ 
	procedure: getting,
	onSuccess: () => redirect('/some-where')
})
 
 
<form action={gettingFA}>
  <input type="number" name="id" value="1992" />
  <input type="string" name="user[name]" value="unnoq" />
  <input type="file" name="user[avatar]" accept="image/*" />
</form>

With Smart Conversion and Bracket Notation, inputs are automatically parsed into the correct types, ensuring smooth data handling. Learn more about Server Action.

Dependency Injection with Context

type ORPCContext = { db: DB, user?: { id: string } }
 
const pub /* public access */ = os.context<ORPCContext>()
 
const getting = pub
	.use((input, context, meta) => {
		if(!context.user){
			throw new ORPCError({ code: 'UNAUTHORIZED' })
		}
		
		return meta.next({
			context: {
				user: context.user // modify user context
			}
		})
	})
	.func((input, context, meta) => {
  // ^ context.user is now guaranteed to be defined
	})

When you use Initial Context, every call to your procedure will require a valid ORPCContext.

Contract-First Development

const gettingContract = oc
	.route({/*something*/})
	.input({/*something*/})
	.output({/*something*/})
	
const getting = os
	.contract(gettingContract)
	.func(async () => 'Worked')

With oRPC's Contract First Development, you can easily separate the procedure's definition from its implementation.

Modern Adapters

oRPC works seamlessly in any environment that supports the Fetch API, including Node.js, Bun, Deno, Next.js, Nuxt.js, Cloudflare Workers, Supabase Functions, and more. We offer first-class serverless support with a dedicated router optimized for cold start performance. Learn more about oRPC's Modern Adapters.

Performance

We focus on both runtime performance and TypeScript checking performance to ensure a developer-first experience. Benchmarks are coming soon!

Reliability

We are committed to delivering robust software. Our aim is 100% test coverage to ensure oRPC's reliability at every level.