Skip to content

IntroductionWhat is Courier?

What is Courier?

A type-safe, framework-agnostic HTTP client with middleware, runtime validation, and adapters for Next.js, Nuxt, Hono, Express, Fastify, and NestJS.

3 min read

@noverax/courier is a type-safe HTTP client that works with any JavaScript framework and any REST backend. Define your API once as plain TypeScript functions — get autocomplete, type inference, runtime validation, and middleware out of the box.

No codegen. No schema files. No changes to your backend.

How It Works

Define your API using courier, defineGroup, and defineApi. Each endpoint declares its response and variables types:

api/sources.ts
import { courier, defineApi, defineGroup } from "@noverax/courier/define"

const { endpoint } = courier()

type User = { id: string; name: string; email: string }

const users = defineGroup({
  prefix: "users",
  endpoints: {
    getAll: endpoint.get("").response<User[]>(),
    getById: endpoint.get(":id").variables<{ id: string }>().response<User>(),
    create: endpoint.post("").variables<{ name: string; email: string }>().response<User>(),
  },
})

const api = defineApi({
  baseURL: "https://api.example.com",
  groups: { users },
})

export { api }

Create a caller and use it with full type inference:

lib/caller.ts
import { createCaller } from "@noverax/courier"
import { api } from "@/api/sources"

const caller = createCaller(api)

const users = await caller.users.getAll()
//    ^? User[]

const user = await caller.users.getById({ id: "1" })
//    ^? User

Two Transport Modes

Both modes share the same API definition and the same type inference:

  • Direct — createCaller(api) — call backends directly from any runtime (Node.js, Bun, Deno, Cloudflare Workers, server components).
  • Proxy — createProxyCaller<typeof api>("api") — browser sends requests through your server, which hides API keys, bypasses CORS, and carries the session cookies you name.

Same definitions, two modes

You define your API once. Both createCaller (direct) and createProxyCaller (proxy) consume the same sources with identical type inference.

Framework Agnostic

The core runs on any JavaScript runtime with fetch. Official adapters handle the server-side proxy for your framework:

FrameworkServer PackageHandler
Next.js@noverax/courier-nextapiRoute()
Nuxt@noverax/courier-nuxtcourierHandler()
Hono@noverax/courier-honocourierHandler()
Express@noverax/courier-expresscourierHandler()
Fastify@noverax/courier-fastifycourierPlugin
NestJS@noverax/courier-nestjscourierMiddleware()

All adapter handlers are zero-arg — they read sources from the core global registry populated by registerSources() (manual) or attachRuntime() (auto-loads courier.config.ts).

On the client side, use createProxyCaller (vanilla) or createProxyQueryCaller (React Query hooks).

Core Philosophy

  • Type safety without codegen — TypeScript functions are the source of truth. No generated files, no build steps.
  • Zero boilerplate — define a method signature and Courier handles serialization, path parameters, headers, and error wrapping.
  • Any backend — works with Express, FastAPI, Rails, Go, Laravel, Django — anything that speaks HTTP.
  • Composable middleware — retry, auth, logging, rate-limiting, caching — or write your own.
  • Standard Schema validation — plug in Zod, Valibot, ArkType, or any Standard Schema library.

Comparison

FeaturefetchaxiostRPCCourier
Type SafetyManualManualFullFull
Framework AgnosticYesYesYesYes
React QueryManualManualBuilt-inBuilt-in
Server ComponentsManualManualSupportedFirst-class
Middleware ChainNoInterceptorsBuilt-inComposable
Runtime ValidationNoNoAny validatorAny Standard Schema
Upload ProgressNoYesNoBuilt-in
Backend RequirementAnyAnytRPC serverAny REST

Next Steps

  1. Installation — add Courier to your project
  2. Quick Start — make your first type-safe API call
  3. Framework Guides — setup for your specific framework