A type-safe RPC library for TypeScript that bridges the gap between your server and client with minimal boilerplate and maximum performance.
- Type-safe by default: Share types between server and client without manual effort.
- Automatic Code Generation: CLI scans your routes and generates client-side calling code and server-side manifests.
- Validation: Integrated with TypeBox for robust runtime validation using TypeScript types.
- Middleware Support: Powered by
alien-middlewarefor powerful, type-safe request context propagation. - Websocket Support: First-class support for websockets via
crossws. - Flexible Formats: Supports JSON, JSON-Seq (for pagination/streaming), and raw Responses.
npm install alien-rpc @sinclair/typebox@0.34Use TypeBox v0.34.x with alien-rpc.
Create a file for your routes (e.g., server/api/hello.ts):
import{route}from'alien-rpc/service'// One path parameterexportconsthello=route('/hello/:name').get(async(name,{},ctx)=>{return{message: `Hello, ${name}!`}})// Multiple path parametersexportconstmultiple=route('/user/:id/post/:postId').get(async([id,postId],{},ctx)=>{return{ id, postId }})// No path parametersexportconstnoParams=route('/status').get(async({},ctx)=>{return{ok: true}})Run the alien-rpc CLI to scan your routes and generate the necessary manifests:
npx alien-rpc './server/api/**/*.ts' --clientOutFile ./client/api.ts --serverOutFile ./server/api.tsTip
See CLI and Configuration for more details.
In your client-side code:
import{defineClient}from'alien-rpc/client'importroutesfrom'./api.ts'constclient=defineClient(routes,{prefixUrl: '/api',})// Parameters can be passed directly if there's only one path paramconstresult=awaitclient.hello('World')console.log(result.message)// "Hello, World!"Use the generated server manifest with any Hattip-compatible adapter. For example, using @hattip/adapter-node:
import{createServer}from'@hattip/adapter-node'import{compileRoutes}from'alien-rpc/service'import{chain}from'alien-rpc/middleware'importroutesfrom'./server/api.ts'consthandler=chain(compileRoutes(routes,{prefix: '/api/',}))createServer(handler).listen(3000)Use route.use() to create factories with shared middlewares. Middlewares can provide context (like a database or user session) to your handlers.
import{chain}from'alien-rpc/middleware'import{route}from'alien-rpc/service'constwithUser=chain(asyncctx=>{constuser=awaitgetUser(ctx.request)return{ user }})constuserRoute=route.use(withUser)exportconstgetProfile=userRoute('/me').get(async(_,ctx)=>{returnctx.user// Type-safe access to user!})Tip
See Middlewares and Context for more details.
Use TypeScript types to define validation rules. The generator automatically converts these to TypeBox schemas.
importtype{t}from'alien-rpc/service'exportconstupdateBio=route('/bio').post(async({ bio }: {bio: string&t.MaxLength<140>})=>{// bio is guaranteed to be <= 140 chars})Path parameters are automatically coerced based on the types defined in your handler's signature. Supported types include string and number.
exportconstgetById=route('/item/:id').get(async(id: number)=>{// id is guaranteed to be a number!})Tip
See Validation and Coercion for more details.
Define websocket routes that share the same connection:
exportconstchat=route.ws(ctx=>{ctx.on('message',msg=>{ctx.send({echo: msg})})})Tip
See Websockets for more details.
Support efficient data transfer for lists and large datasets.
exportconstlistItems=route('/items').get(asyncfunction*({ offset =0}){constitems=awaitdb.items.findMany({skip: offset,take: 10})for(constitemofitems)yielditemreturnpaginate(this,{next: {offset: offset+10},})})Tip
See Pagination and Streaming for more details.
Check out the examples directory for minimal, ready-to-run projects using different stacks:
- Astro: Integration with Astro's server-side routes and client-side components.
- Vite + Node.js: A full-stack setup with a Node.js backend proxied through Vite.
- Vite + Cloudflare Workers: Deploying to Cloudflare Workers using Vite and
@cloudflare/vite-plugin.
For a package-local example of the umbrella subpath imports, see
examples/full-stack.ts.
For larger projects, use an alien-rpc.config.ts file:
import{defineConfig}from'alien-rpc/config'exportdefaultdefineConfig({include: ['./server/api/**/*.ts'],outDir: './src/generated',clientOutFile: 'client.ts',serverOutFile: 'server.ts',})Tip
See CLI and Configuration for more details.
MIT