Import
import { defineApi } from "@noverax/courier/define"Signature
declare function defineApi<G extends OwnGroups>(
options: BaseOptions & { groups: G }
): DefinedApi<G>groups takes any name but raw or with, and the flat endpoints any name but options, raw or with: see Reserved names. A reserved name is a type error and throws ConfigurationError("RESERVED_ENDPOINT_NAME") when the source is defined.
Parameters
BaseOptions
The core configuration for a data source:
type BaseOptions = {
baseURL?: string
responseType?: "json" | "text" | "blob" | "arrayBuffer" | "formData"
responseParser?: (response: Response) => Promise<unknown>
defaultBody?: Record<string, unknown>
defaultQueryParams?: Record<string, unknown>
allowedHeaders?: string[]
bodySerializer?: (data: Record<string, unknown>) => BodyInit
searchParamsSerializer?: SearchParamsSerializer
deduplicate?: boolean
middleware?: Middleware[]
pathOptions?: PathOptions
timeout?: number
headers?: HeadersInit
signal?: AbortSignal
session?: SessionOptions
forward?: ForwardOptions
}Basic Usage
With Groups
import { defineApi } from "@noverax/courier/define"
import { users } from "./users"
import { posts } from "./posts"
const api = defineApi({
baseURL: process.env.API_URL ?? "http://localhost:4000",
groups: { users, posts },
})
export const sources = { api }
export type Sources = typeof sourcesWith Options
import { defineApi } from "@noverax/courier/define"
import { logMiddleware, authMiddleware } from "@noverax/courier/middleware"
const api = defineApi({
baseURL: process.env.API_URL ?? "http://localhost:4000",
// Response parsing (default: "json")
responseType: "json",
// Default data merged into every POST/PUT/PATCH body
defaultBody: { apiKey: process.env.API_KEY },
// Default params merged into every request
defaultQueryParams: { locale: "en" },
// Request timeout in milliseconds
timeout: 10_000,
// Static headers
headers: {
"X-Client": "courier",
Authorization: `Bearer ${process.env.API_SECRET}`,
},
// Middleware chain
middleware: [
logMiddleware(),
authMiddleware({ getToken: () => getAuthToken() }),
],
// Enable request deduplication
deduplicate: true,
groups: { users, posts },
})Options Reference
baseURL
Sets the base URL prefix for all requests from this source.
responseType
Controls response parsing. Supported: "json" (default), "text", "blob", "arrayBuffer", "formData". A 204 status always returns null. A proxy client resolves to the same kind of value as a direct call.
responseParser
Custom response parser that overrides responseType:
const api = defineApi({
baseURL: "...",
responseParser: async (response) => {
const text = await response.text()
return new DOMParser().parseFromString(text, "text/xml")
},
groups: { users },
})defaultBody
Object merged into every POST/PUT/PATCH body:
const api = defineApi({
baseURL: "...",
defaultBody: { apiKey: "...", version: "v2" },
groups: { users },
})
// POST /users body: { apiKey: "...", version: "v2", name: "Alice" }defaultQueryParams
Object merged into every request's search parameters:
const api = defineApi({
baseURL: "...",
defaultQueryParams: { locale: "en", format: "json" },
groups: { users },
})
// GET /users → GET /users?locale=en&format=jsonmiddleware
Array of middleware functions applied to every request. See Middleware.
bodySerializer
Custom body encoder (e.g., for URL-encoded forms):
const api = defineApi({
baseURL: "...",
bodySerializer: (data) =>
new URLSearchParams(Object.entries(data).map(([k, v]) => [k, String(v)])),
groups: { users },
})searchParamsSerializer
Custom search params encoder:
import qs from "qs"
const api = defineApi({
baseURL: "...",
searchParamsSerializer: {
stringify: (obj) => qs.stringify(obj),
parse: (str) => qs.parse(str),
},
groups: { users },
})deduplicate
Cancel a read still in flight when the same endpoint is read again. Writes are never cancelled. See Abort & Dedup.
allowedHeaders
Additional header names the proxy carries for every endpoint of the source, in both directions: forwarded from the browser to the backend, and passed back from the backend's answer to the browser. An endpoint names its own with .headers([...]), which adds to this list rather than replacing it. The lists govern only what the proxy passes along: headers the source, a middleware or an endpoint's own closure sets are written by you and are always sent.
const users = defineGroup({
prefix: "users",
endpoints: {
list: endpoint.get("").headers(["x-total-count", "link"]),
create: endpoint.post("").headers(["location"]),
},
})Cookies are named the same way, one direction at a time: cookie forwards the caller's cookies to the upstream, and set-cookie passes the upstream's back. Neither crosses where it is not named — see Cookies. An API that keeps a session names its cookies there instead, and refuses cookie and set-cookie in these lists with SESSION_HEADER_CONFLICT: they would carry every cookie past it.
The headers that describe the body — Content-Type, Content-Length, Content-Encoding — are never taken from these lists, because the proxy sends a body of its own.
session
How the API keeps the user's session: the cookies it is kept in, by name or by a prefix ending in *; a cookie holding a bearer token; the double-submit token a write repeats; whether the session cookies it sets reach the browser; the other origins whose pages may make writes with it. A call made while a server serves a user carries that user's session from the request being served; a call made in a browser lets the browser send its own cookies.
defineApi({
baseURL: "https://api.example.com",
session: {
cookies: ["sessionid", "logto_*"],
bearer: "access_token",
csrf: { cookie: "csrftoken", header: "x-csrftoken" },
},
endpoints,
})An endpoint takes it with session: false when it answers every user alike, and session: "unguarded" for a write that does no harm when another site sets it off, such as signing out. See User Sessions.
forward
The headers a call made while serving a request carries from it: requestId, trace, clientAddress, userAgent, locale, and further headers by name. An endpoint keeps them off its calls with forward: false. See User Sessions.
Related
- User Sessions — the session and the forwarded headers, end to end
- defineGroup — defining endpoint groups
- Defining APIs Guide — in-depth usage patterns
- Middleware — middleware reference