# Singleton Pattern

Build each client once, in a module of its own, and import it wherever a call is made.

## The pattern

Every Courier factory learns your API's type in one place: from the API object you pass to `createCaller` or `createQueryCaller`, or from the routes you pass to `createProxyCaller` and `createProxyQueryCaller`. Build each client once, in a module of its own, and export it. Components, loaders and server code import that client, so none of them repeats an import or a default, and a change to the API reaches every call site through that one module.

```ts
// Without it, every file builds its own client and repeats its options:
import { createProxyCaller } from "@noverax/courier"
import { apiRoutes } from "@/courier.routes"

const users = await createProxyCaller(apiRoutes, { timeout: 10_000 }).users.getAll()
```

```ts
// With it, every file imports the one client:
import { caller } from "@/lib/caller"

const users = await caller.users.getAll()
```

## Step-by-step setup

### Step 1: Define your API

Create your groups and API definition with `courier`, `defineGroup` and `defineApi`, and mark what a page may call through the proxy with `expose`:

```ts title="api/users.ts"
import { courier, defineGroup } from "@noverax/courier/define"

const { endpoint } = courier()

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

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

```ts title="api/index.ts"
import { defineApi } from "@noverax/courier/define"
import { users } from "./users"

export const api = defineApi({
  name: "api",
  baseURL: process.env.API_URL ?? "http://localhost:4000",
  groups: { users },
})
```

### Step 2: Build each client once

Give each client a module that builds it and exports it. In the browser, pass the routes Courier writes into `courier.routes.ts` from your marks — see [Exposing Endpoints](/docs/proxy-mode/expose). They carry every group, endpoint, variable and response type a page may use, and how each endpoint is reached, so the definition itself — its upstream host, its middleware, every endpoint path — stays out of the bundle.

```ts title="lib/caller.ts"
import { createProxyCaller } from "@noverax/courier"
import { apiRoutes } from "@/courier.routes"

export const caller = createProxyCaller(apiRoutes)
```

```ts title="lib/query.ts"
import { createProxyQueryCaller } from "@noverax/courier-react"
import { apiRoutes } from "@/courier.routes"

export const q = createProxyQueryCaller(apiRoutes)
```

The proxy serves only what the marks open, whichever client calls it.

On the server, where the definition is already present, pass the API object and the type comes with it:

```ts title="lib/server-caller.ts"
import { createCaller } from "@noverax/courier"
import { api } from "@/api"

export const serverCaller = createCaller(api, { timeout: 10_000 })
```

### Step 3: Import the client

Every call site imports the client and gets full autocomplete for group names, method names and variable types:

```ts title="app/users/[id]/page.tsx"
import { serverCaller } from "@/lib/server-caller"

export default async function UserPage({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params
  const user = await serverCaller.users.getById({ id: Number(id) })
  //    ^? User
  return <h1>{user.name}</h1>
}
```

## Using with React Query

The pattern pays off most with `createProxyQueryCaller`, whose hooks are used throughout an app. Each component imports `q` and nothing else:

```tsx title="components/user-list.tsx"
"use client"

import { q } from "@/lib/query"

export function UserList() {
  // "users" is a known group, "getAll" a known method, and data is User[]
  const { data } = q.users.useQuery("getAll")

  const { mutate } = q.users.useMutation("create", {
    onSuccess: (user) => {
      // user is typed as User
      console.log(user.name)
    },
  })

  return (
    <div>
      {data?.map((user) => <p key={user.id}>{user.name}</p>)}
      <button onClick={() => mutate({ name: "Jane", email: "jane@test.com" })}>
        Add User
      </button>
    </div>
  )
}
```

## Multiple sources

If your app connects to several backends, build one client per source, each named after it:

```ts title="lib/query.ts"
import { createProxyQueryCaller } from "@noverax/courier-react"
import { mainApiRoutes, paymentApiRoutes } from "@/courier.routes"

export const mainQ = createProxyQueryCaller(mainApiRoutes)
export const paymentQ = createProxyQueryCaller(paymentApiRoutes)

// mainQ.users.useQuery("getAll")      ← knows mainApi's groups
// paymentQ.products.useQuery("list")  ← knows paymentApi's groups
```

> **One module, not a global declaration**
>
> Courier keeps no global registry of API types, and a factory never looks one up: the type comes from the API object or the routes. The module that builds a client is therefore the single place its type is named. When you add a group or a method, every `q.{group}.useQuery()` and `caller.{group}.{method}()` that imports the client sees it at once.

## Next steps

- [Project Setup](/docs/project-setup) — recommended file structure, with the client modules in `lib/`
- [Exposing Endpoints](/docs/proxy-mode/expose) — what a page may call, and the routes that keep the definition out of the bundle
- [Options](/docs/advanced/options) — source-level and per-request configuration
