Skip to Content
DocsGetting Started

Getting Started with Actyx RPC

Actyx RPC is a type-safe RPC framework for building composable server actions in TypeScript. It allows you to build standard, type-safe API boundaries and hooks for React applications with minimal boilerplate and built-in caching.

  • 🔒 End-to-end type safety: Type inference flows from server procedures directly to client hooks.
  • ⚡ Built for server actions: Works seamlessly with Next.js Server Actions and standard route handlers.
  • 🧩 Composable middleware & plugins: Chain middlewares to extend context and validate payloads.
  • 🧠 Flexible input modes: Configure strict, partial, or form data input schemas.
  • 🛡️ Resilience: Out-of-the-box support for Retries, Timeouts, Rate Limits, and Circuit Breakers.
  • 📊 Observability: Built-in OpenTelemetry instrumentation and telemetry plugin tracking.
  • 🔌 Resolver agnostic: Integrates with Zod, Valibot, ArkType, Joi, Yup, or custom validation functions.

Why Actyx?

Traditional APIs force you to choose between flexibility and type safety. Actyx gives you both.

Define a procedure once, and get:

  • Fully typed inputs
  • Validated payload structures
  • Reusable logic layers across your application

No code generation steps. No duplicate schemas leaking across directories. Just clean, predictable, and typed server actions.


Package Architecture

Actyx RPC is split into two specialized, lightweight packages:

PackagePurposeTypical Environment
@explita/actyx-rpcCore RPC engine, procedure builders, middleware, execution policies, validation resolvers, and server adaptersNode.js, Next.js Server Actions, Express, Cloudflare Workers
@explita/actyx-rpc-reactReact integration, built-in QueryClient, in-memory cache, reactive hooks (useQuery, useMutation, etc.), and streaming clientsNext.js Client Components, Vite, React SPA

Installation

Choose the packages you need based on your architecture:

Fullstack (Next.js, Remix, TanStack Start)

Install both packages to build server procedures and consume them in React:

npm install @explita/actyx-rpc @explita/actyx-rpc-react

Backend / Server API Only

If you are building a standalone API or server actions without React client hooks:

npm install @explita/actyx-rpc

Frontend / React App Only

If you are consuming Actyx RPC procedures from a React client application:

npm install @explita/actyx-rpc-react

Peer Dependencies

Install your schema validation library of choice (e.g. zod, valibot, arktype, joi, or yup):

npm install zod

Quick Start

1. Define Server Procedures (@explita/actyx-rpc)

import { createProcedure } from "@explita/actyx-rpc"; import { z } from "zod"; import { zodResolver } from "@explita/actyx-rpc/resolvers/zod"; const procedure = createProcedure({ async createContext() { return { ok: true, ctx: { userId: "user_123", role: "admin", }, }; }, }); // Create a query procedure export const getPost = procedure .input( zodResolver( z.object({ id: z.string().min(1, "Post id is required"), }), ), ) .query(async ({ ctx, input }) => { return { id: input.id, title: "Hello from Actyx RPC", authorId: ctx.userId, }; });

2. Consume in React (@explita/actyx-rpc-react)

Wrap your application in ActyxProvider and call the procedure directly in components:

"use client"; import { QueryClient, ActyxProvider, useQuery } from "@explita/actyx-rpc-react"; import { getPost } from "@/server/procedures"; const queryClient = new QueryClient({ queries: { staleTime: "5m", // cache fresh for 5 minutes }, }); export function App() { return ( <ActyxProvider client={queryClient}> <PostDetail postId="post_1" /> </ActyxProvider> ); } function PostDetail({ postId }: { postId: string }) { const { data: post, isLoading, isError, error } = useQuery( () => getPost({ id: postId }), { queryKey: ["post", postId] } ); if (isLoading) return <div>Loading post...</div>; if (isError) return <div>Error: {error?.message}</div>; return <h1>{post.title}</h1>; }

3. Or Use the Client Proxy & API Endpoint

If you prefer calling your procedures via an HTTP API endpoint (/api/rpc/...) rather than direct Server Action imports:

// 1. Define your router (backend/router.ts) import { createRouter } from "@explita/actyx-rpc"; import { getPost } from "./procedures"; export const appRouter = createRouter({ posts: createRouter({ get: getPost, }), }); export type AppRouter = typeof appRouter;
// 2. Mount the router in Next.js App Router (app/api/rpc/[...rpc]/route.ts) import { createHandler } from "@explita/actyx-rpc/adapters/next"; import { appRouter } from "@/backend/router"; export const { GET, POST } = createHandler(appRouter);
// 3. Initialize the client proxy (lib/rpc.ts) import { createClient } from "@explita/actyx-rpc-react"; import type { AppRouter } from "@/backend/router"; export const rpc = createClient<AppRouter>({ baseUrl: "/api/rpc" }); // 4. Call procedures directly or with hooks anywhere in your app: function MyComponent() { const { data: post, isLoading } = rpc.posts.get.useQuery({ id: "post_1" }); if (isLoading) return <div>Loading...</div>; return <h1>{post?.title}</h1>; }

Documentation Sections


Support the Mission

Actyx RPC is built to simplify building type-safe, distributed systems with minimal boilerplate. If it has helped you build better APIs faster, please consider supporting the project to ensure its continued growth and maintenance!


License

MIT © Explita 

Last updated on