Full-stack frameworks
Server and browser calls
Set up a client for the browser and another for code that runs on the server.
On this page
A browser needs to send a request to call your API. Server code can call the implementation directly when it’s already running in the same process.
Both clients use the same contract. Here’s how to set up each one to load a user.
Create a client with fetchTransport(). It sends requests to the paths in your contract:
import { createClient } from '@hulla/api/client'
import { fetchTransport } from '@hulla/api/fetch'
import { contract } from './contract'
export const api = createClient(contract, {
transport: fetchTransport(),
})Import this client into your UI and call the route:
import { api } from './api/browser-client'
const result = await api.users.byId({ params: { id: 'ada' } })
if (result.status === 200) console.log(result.body.name) // AdaThe server must expose the contract at an HTTP endpoint. The framework pages below show where to mount it. For an API on another origin, set baseUrl to that origin.
Use inProcessTransport() when your server code can import the implementation. It calls the handler without making an HTTP request back to your own server.
import { createClient } from '@hulla/api/client'
import { inProcessTransport } from '@hulla/api/in-process'
import { contract } from './contract'
import { implementation } from './server'
export const api = createClient(contract, {
transport: inProcessTransport(implementation),
})This example uses src/api/server.ts from Server implementations. Import the local client into a server-rendered page or a background job and make the same api.users.byId(...) call.
Validation and server middleware still run. HTTP route matching and endpoint middleware don’t, because this call never reaches the HTTP endpoint.
Use Fetch when the API runs in another process or deployment. Use it when you need the endpoint’s HTTP middleware or response headers, too.
In Node.js and other server runtimes, provide an absolute URL:
const api = createClient(contract, {
transport: fetchTransport({ baseUrl: 'https://api.example.com' }),
})Some frameworks supply a request-aware fetch that can resolve relative URLs. Follow the framework’s integration page when using that API.
A framework server function can use the local client inside its callback. The framework handles the browser-to-server request; the local client then runs your API implementation in that callback.
Use this for operations within your app. Keep an HTTP endpoint when mobile apps, other services, or generated clients need to call the API independently.
| Framework | Start here |
|---|---|
| Next.js | App Router Route Handlers; caching and invalidation |
| TanStack Start | Server routes and server functions |
| React Router | Resource routes, loaders, and actions |
| SolidStart | API routes, queries, and actions |
| SvelteKit | Endpoints and load; remote functions |
| Nuxt | Nitro routes and useAsyncData |
| Astro | Endpoints, components, and server islands |
For response handling and client middleware, continue to Calling the API.