Import
import { createCaller } from "@noverax/courier"Signature
createCaller takes your API definition and optional defaults, returning a TypedClient. Direct mode calls the backend directly without going through a proxy.
import type { CallerDefaults, TypedClient } from "@noverax/courier"
import type { DefinedApi } from "@noverax/courier/define"
declare function createCaller<TApi extends DefinedApi>(
api: TApi,
defaults?: CallerDefaults,
): TypedClient<TApi>Parameters
api
An API source definition created with defineApi().
import { api } from "@/api"
const caller = createCaller(api)CallerDefaults
Optional default settings applied to every request.
type CallerDefaults = {
timeout?: number
headers?: Record<string, string>
context?: Context
onError?: (error: HTTPError) => void | Promise<void>
onSuccess?: (data: unknown) => void | Promise<void>
ws?: CallerWSOptions
}
type CallerWSOptions = {
auth?: WSAuthParams | (() => WSAuthParams | undefined | Promise<WSAuthParams | undefined>)
backoff?: { initialMs?: number; maxMs?: number; factor?: number; jitter?: boolean }
heartbeat?: { intervalMs?: number; timeoutMs?: number }
messageQueue?: { maxCount?: number; maxBytes?: number }
protocols?: string[]
maxMessageSize?: number
}
type WSAuthParams = Record<string, unknown>ws— options of the sockets this caller's channels and push subscriptions open; see Connection options.
Return Type
A call resolves to the response data. Through .raw it resolves to the whole response, HttpResponse<R>, with status, headers and data; with .with({ stream: true }) the data is the unread ReadableStream.
type HttpResponse<T> = {
status: number
headers: Headers
data: T
}Basic Usage
import { createCaller } from "@noverax/courier"
import { api } from "@/api"
export const caller = createCaller(api, {
timeout: 10000,
})
// Usage: caller.group.method(variables?)
const result = await caller.users.getAll()
const user = await caller.users.getById({ id: "123" })Streams and channels
A direct caller opens a stream or a channel at the API's baseURL, as it sends any other call there. One served by this process with .implement() is not at baseURL — that is the upstream's address — so calling it through createCaller throws ConfigurationError("DIRECT_CALL_TO_SERVED_ENDPOINT"), naming the endpoint, when it is called; the rest of the API stays callable. Reach it through createProxyCaller and the server that serves it — see Streams & Channels.
Passing Context
defaults.context is a Context object. Use it to forward headers from the incoming request to the backend: authorization, user-agent, referer, origin and x-request-id, the headers the source and the endpoint name, and the user's cookies where they name cookie. A context belongs to one incoming request, so build the caller for that request:
type Context = {
req?: Request
res?: Response
headers?: Headers
}import { createCaller } from "@noverax/courier"
import { api } from "@/api"
export async function GET(req: Request) {
// The session the API describes is read from this request
const caller = createCaller(api, { context: { req } })
const profile = await caller.users.getProfile()
// Headers of your own, for one call
const user = await caller.users
.with({ headers: { "x-tenant": "acme" } })
.getById({ id: "123" })
return Response.json({ profile, user })
}An API that keeps a session takes the user's session from the request a context names, and a framework adapter finds that request without one — see User Sessions. Handing a caller await headers() whole sends the API every header and every cookie the browser addressed to your application.
Server Components
Direct mode is designed for server-only execution. Use it in React Server Components, API routes, server actions, or any backend runtime.
import { caller } from "@/lib/caller"
export default async function UsersPage() {
const result = await caller.users.getAll()
return (
<ul>
{result.data?.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}Server Actions
"use server"
import { caller } from "@/lib/caller"
import { revalidatePath } from "next/cache"
export async function createUser(formData: FormData) {
const name = formData.get("name") as string
await caller.users.create({ name })
revalidatePath("/users")
}Configuring Defaults
const caller = createCaller(api, {
timeout: 15000,
headers: {
"X-Internal-Service": "courier-app",
},
onError: (error) => {
console.error(`[Direct] HTTP ${error.status}:`, error.data)
},
})Related
- Server-Side Usage — direct mode architecture
- Server Actions — mutations with revalidation
- createProxyCaller — proxy-mode alternative for client components
- defineApi — defining data sources