bacali95/typesafe-rpc
Overview
This skill is a type-safe RPC library for Node.js and TypeScript that provides end-to-end types between server and client. It defines a schema of entities and operations, generates a typed client proxy, and exposes a server handler factory with hooks, routes, and middleware support.
How this skill works
You declare an api schema as a nested object: entity -> operation -> handler. Handlers are typed functions that receive { params, context } and return a Promise of the result. The server exposes createRpcHandler to accept POST requests with { entity, operation, params } and run the matching handler; the client uses createRpcClient to call endpoint methods like client.entity.operation(params, signal?).
When to use it
- Adding new RPC operations or editing existing ones in the schema.
- Wiring server-side request handling for typed RPC endpoints.
- Generating or using a fully typed client proxy in TypeScript or React.
- Implementing handlers that require request-aware context (e.g., auth).
- Composing middleware and routes for access control or input validation.
Best practices
- Define your api schema with
as constso the client receives literal types and inference is exact. - Extend BaseContext to include app-specific fields (userId, db, etc.) and pass context.request for each server call.
- Use Route with middleware chaining for clear AND/OR semantics: chained .middleware calls are AND, .middleware(a, b) uses OR semantics via orMiddleware.
- Provide a custom errorHandler in createRpcHandler to return structured error responses instead of a generic 500.
- Prefer the type definitions in the library’s shared types for exact Handler/Args shapes when implementing handlers.
Example use cases
- Implementing an items.getById handler that returns { name } and using the typed client to call it from React.
- Building authentication middleware that attaches userId to context and can be reused across routes.
- Composing authorization rules with orMiddleware so anonymous or token-based access can both succeed.
- Wiring a server endpoint that reads POST bodies from Fetch or Express and runs pre/post hooks for logging.
- Creating a typed client in the frontend that calls endpoint?entity::operation and benefits from compile-time params checks.
FAQ
POST requests must contain JSON body { entity, operation, params }. The server expects context.request and a POST method.
How do I get full type inference on the client?
Pass the schema type to createRpcClient (e.g., createRpcClient<typeof apiSchema>(...)) and declare the schema with as const so literal types are preserved.