Start here
Installation
Install the core package and the integration for your server.
On this page
Install @hulla/api to define a contract and create a client or server. Add an adapter package if you’re serving the API through a framework or platform listed below.
- 1Install the validated core
Start with @hulla/api and a runtime schema library. Run this in the workspace package that owns the shared contract. The examples use Zod, so installing it now gives you the recommended runtime-validated path through the quick start.
Terminalbun add @hulla/api@2.0.0 zodTerminalnpm install @hulla/api@2.0.0 zodTerminalpnpm add @hulla/api@2.0.0 zodTerminalyarn add @hulla/api@2.0.0 zodOptional agent skill
Type-only declarations are also available for already-trusted values. They preserve compile-time types but do not validate real input or output. See contract declarations when that tradeoff is intentional.
- 2Connect the server boundary
Mount the implementation into the host that receives requests. A server adapter translates the host’s native request into a contract call, provides request context, and turns the declared result back into the host’s response. Only server code imports this adapter.
- Web-standard hosts: use the built-in
@hulla/api/fetchentrypoint. Follow the runtime guides for Bun, Deno, Vercel, and other Fetch-compatible hosts. - Native servers:
@hulla/api-node,@hulla/api-express,@hulla/api-fastify,@hulla/api-hono,@hulla/api-h3,@hulla/api-elysia,@hulla/api-koa, or@hulla/api-nestjs. - Full-stack frameworks:
@hulla/api-next,@hulla/api-tanstack-start,@hulla/api-react-router,@hulla/api-solid-start,@hulla/api-sveltekit,@hulla/api-nuxt, or@hulla/api-astro.
Install one server adapter in the application that owns the listener or framework route. The shared contract package should not depend on Express, Next.js, Node.js, or another host.
- Web-standard hosts: use the built-in
- 3Connect the client boundary
Choose how typed calls travel from the caller to the server. A client transport carries a contract-shaped call. It does not mount routes or own server middleware. Most applications begin with one of the two transports already included in
@hulla/api.- Same process: use
@hulla/api/in-processin server-rendered code, tests, or another trusted process that can import the implementation. - HTTP: use
@hulla/api/fetchin browsers, mobile clients, services, and server-side HTTP calls. - Workers or desktop IPC: install
@hulla/api-message-port. - Dedicated socket: install
@hulla/api-websocket.
The client imports the contract and its chosen transport. It imports the implementation only for an in-process call—never for Fetch, MessagePort, or WebSocket.
Once direct calls work, optionally add
@hulla/api-control,@hulla/api-tanstack-query, or@hulla/api-swr. These wrap an existing client; they do not replace its transport. - Same process: use
- 4Verify one vertical slice
Prove the contract, implementation, and client before adding framework setup. Build the first route in process so a listener, proxy, or framework cannot hide contract mistakes. The quick start gives you three small modules:
contract.tsdeclares and validates what crosses the boundary.implementation.tscontains server-only behavior.example.tscreates a typed client, calls the route, and prints the created task.
When that call works, the core setup is complete. Keep the in-process transport when the caller stays beside the server. For a browser or remote caller, move on to the client transport from step 3 and mount the same implementation with the server adapter from step 2.