Skip to content

API ReferenceTypes

Types

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

3 min readboth 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>
}

ProxyCallerDefaults

Options for createProxyCaller and createProxyQueryCaller.

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 ProxyCallerDefaults = {
  proxyPath?: ProxyPath
  source?: object
  timeout?: number
  credentials?: RequestCredentials
  headers?: Record<string, string>
  onError?: (error: HTTPError) => void | Promise<void>
  onSuccess?: (data: unknown) => void | Promise<void>
}

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
  deduplicate?: boolean
  middleware?: Middleware[]
  expose?: boolean
  boundary?: ProxyBoundary
  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 }