Skip to content

API ReferenceTypes

Types

Complete reference for all TypeScript types exported by @noverax/courier.

4 min readUpdated both modes

Overview

All TypeScript types used across the Courier library, organized by category.

Client Types

TypedClient

The main typed client returned by createCaller and createProxyCaller. Provides dot-notation access to groups, .raw for full response, and .with() for per-request options.

ts
type TypedClient<T extends DefinedApi<any>, Raw extends boolean = false> = {
  [K in keyof ApiGroups<T>]: GroupClient<ApiGroups<T>[K], Raw>
} & {
  raw: TypedClient<T, true>
  with(options: WithOptions): TypedClient<T, Raw>
}

GroupClient

A group with typed endpoint methods, .raw accessor, and .with(): one method per endpoint of the group, typed from its variables and response, beside these two.

ts
type GroupClient<G, Raw extends boolean = false> = {
  // …one method per endpoint of G…
  raw: GroupClient<G, true>
  with(options: WithOptions): GroupClient<G, Raw>
}

WithOptions

Options for .with() — all CallOptions except rawData (controlled via .raw).

ts
type WithOptions = Omit<CallOptions, "rawData">

CallerDefaults

Options for createCaller.

ts
type CallerDefaults = {
  timeout?: number
  headers?: Record<string, string>
  context?: Context
  onError?: (error: HTTPError) => void | Promise<void>
  onSuccess?: (data: unknown) => void | Promise<void>
}

ProxyCallerOptions

Options for createProxyCaller and createProxyQueryCaller, which take them after the routes of an API.

ts
type ProxyPath = {
  http?: string  // default: "/api/request"
  sse?: string   // default: "/api/sse"
  ws?: string    // no default — throws if WS endpoint invoked without it
}

type ProxyCallerOptions = {
  proxyPath?: ProxyPath
  timeout?: number
  cancelPrevious?: readonly string[]
  coalesce?: boolean
  credentials?: RequestCredentials
  headers?: Record<string, string>
  onError?: (error: HTTPError) => void | Promise<void>
  onSuccess?: (data: unknown) => void | Promise<void>
  ws?: CallerWSOptions
}

CourierRoutes

The routes of one API that a page's proxy client takes: the source the server registered it as, and each endpoint a page may call, by its path, with how the page reaches it. Courier writes them into courier.routes.ts from the endpoints marked .expose() — see Exposing Endpoints.

ts
import type { CourierRoutes, RouteMap, RouteShape } from "@noverax/courier/define"

// RouteShape: { type: "query"; get?: true } | { type: "mutation" }
//           | { transport: "sse" } | { transport: "ws"; bidirectional?: true }
// RouteMap<TApi>: the paths of TApi, each in the shape the API declares it
// CourierRoutes<TApi, TEndpoints>: { source, endpoints, cancelPrevious?, coalesce? }

Endpoint Definition Types

Endpoint Types

An endpoint is an inert descriptor, not a callable. One of three types names it, according to the head it was built from:

ts
import type {
  AnyEndpoint,      // any of the three, when the kind does not matter
  ChannelEndpoint,  // ChannelEndpoint<Response, Variables, Send>
  HttpEndpoint,     // HttpEndpoint<Response, Variables, Kind extends "query" | "mutation">
  StreamEndpoint,   // StreamEndpoint<Response, Variables>
} from "@noverax/courier/define"

Inference Helpers

Read the types back off an endpoint. This is how a caller knows what a method takes and returns:

ts
import type { InferClient } from "@noverax/courier" // InferClient<TApi>: the client createCaller(api) returns
import type {
  InferKind,          // InferKind<E>: "query" | "mutation" | "stream" | "channel"
  InferResponse,      // InferResponse<E>: what the endpoint resolves to
  InferSend,          // InferSend<E>: what a channel accepts from the client
  InferVariables,     // InferVariables<E>: what the caller must pass
  InferVariablesOut,  // InferVariablesOut<E>: what its closure receives, once a schema has validated the call
} from "@noverax/courier/define"

The clients infer all of this themselves; name a type with these helpers where one is needed elsewhere — a component's props, a test's fixture.

Wire Surfaces

The object .via() hands the closure. Name these to lift a closure out of the chain into a function of its own:

ts
interface HttpWire {
  get(path?: string, variables?: unknown): WireCall
  post(path?: string, body?: unknown): WireCall
  put(path?: string, body?: unknown): WireCall
  patch(path?: string, body?: unknown): WireCall
  del(path?: string, variables?: unknown): WireCall
  head(path?: string, variables?: unknown): WireCall
}

interface StreamWire {
  get(path?: string, variables?: unknown): WireCall
}

interface ChannelWire {
  connect(path?: string, variables?: unknown): WireCall
}

type HttpProbe<Variables> = (http: HttpWire, variables: Variables) => WireCall
type StreamProbe<Variables> = (sse: StreamWire, variables: Variables) => WireCall
type ChannelProbe<Variables> = (ws: ChannelWire, variables: Variables) => WireCall
ts
import type { HttpProbe } from "@noverax/courier/define"

const byId: HttpProbe<{ id: string }> = (http, vars) => http.get(":id", vars)

const getById = endpoint.variables<{ id: string }>().via(byId).response<User>()

Response Types

HttpResponse

ts
type HttpResponse<T = unknown> = {
  status: number    // HTTP status code
  headers: Headers  // Response headers
  data: T           // Parsed response body
}

Options Types

CallOptions

Per-request options passed when calling an endpoint.

ts
type CallOptions<R extends boolean = false> = Omit<RequestInit, "method" | "body"> & {
  timeout?: number
  responseType?: "json" | "text" | "blob" | "arrayBuffer" | "formData"
  onDownloadProgress?: (progress: { percent: number; transferredBytes: number; totalBytes: number }) => void
  onUploadProgress?: (progress: UploadProgress) => void
  rawData?: R
  forwardVariables?: boolean
  stream?: boolean
  method?: "GET" | "POST"
}

headers, signal, credentials and the rest of RequestInit are passed to fetch; method is the verb a call goes to the proxy with, not the endpoint's own.

UploadProgress

ts
type UploadProgress = {
  percent: number           // 0 to 1
  transferredBytes: number
  totalBytes: number
}

MethodParameters

ts
type MethodParameters<T extends HttpMethod> = T extends "head"
  ? [url?: string, options?: MethodOptions<T>]
  : [url?: string, variables?: Record<string, unknown>, options?: MethodOptions<T>]

Source & Config Types

BaseOptions

Core configuration for defineApi().

ts
type BaseOptions = MergeOptions & FetchOptions & {
  baseURL?: string
  responseType?: ResponseType
  responseParser?: (response: Response) => Promise<unknown>
  defaultBody?: Record<string, unknown>
  defaultQueryParams?: Record<string, unknown>
  allowedHeaders?: string[]
  bodySerializer?: (data: Record<string, unknown>) => BodyInit
  searchParamsSerializer?: SearchParamsSerializer
  cancelPrevious?: readonly string[]
  middleware?: Middleware[]
  expose?: boolean
  proxyPath?: ProxyPath
}

GroupOptions

ts
type GroupOptions = MergeOptions & {
  prefix?: string
}

ResponseType

ts
type ResponseType = "json" | "text" | "blob" | "arrayBuffer" | "formData"

SearchParamsSerializer

ts
type SearchParamsSerializer = {
  stringify: (obj: Record<string, unknown>) => string
  parse: (str: string) => Record<string, unknown>
}

Context

ts
type Context = {
  req?: Request
  res?: Response
  headers?: Headers
  proxyMethod?: "GET" | "POST"
}

PathOptions

ts
type PathOptions = (request?: Request) =>
  Record<string, boolean | number | string | null | undefined> | undefined
  | Promise<Record<string, boolean | number | string | null | undefined> | undefined>

Middleware Types

Middleware

ts
type Middleware =
  | { name: string; phase: "before"; handler: BeforeHandler }
  | { name: string; phase: "after"; handler: AfterHandler }
  | { name: string; phase: "wrap"; handler: WrapHandler }

defineMiddleware(name, phase, handler) builds one — see Middleware.

MiddlewareContext

ts
type MiddlewareContext = {
  source: string
  group?: string
  method: string
  variables?: unknown
  headers: Headers
  transport?: "http" | "sse" | "ws"
  request?: Request
  kind?: "query" | "mutation"
  carriesSession?: boolean
  sessionPartition?: string
  signal?: AbortSignal
  purpose?: CallPurpose
}

type CallPurpose = "csrf-token"

purpose is set on a request the session makes rather than an endpoint's call — "csrf-token" for the double-submit token a write asks for — whose method names no endpoint. See Middleware for each field.

MiddlewareResponse

ts
type MiddlewareResponse = {
  status: number
  data: unknown
  headers: Headers
}

NextFn

ts
type NextFn = () => Promise<MiddlewareResponse>

React Query Types

ts
type QueryOptions<R, V> = WithVariables<V> &
  Omit<UseQueryOptions<R, Error, R, QueryKey>, "queryKey" | "queryFn"> & {
    requestOptions?: CallOptions
  }

type MutationOptions<R, V> = Omit<
  UseMutationOptions<R, Error, MutationVariables<V>>,
  "mutationFn"
> & {
  requestOptions?: CallOptions
}

type MutationVariables<V> = [V] extends [undefined]
  ? void
  : {} extends V ? void | V : V

type WithVariables<V> = [V] extends [undefined]
  ? { variables?: undefined }
  : {} extends V
    ? { variables?: V }
    : { variables: V }