# Quick Start

Set Courier up in one command, or by hand in three steps: define your API, set up a caller, make your first typed request.

## In one command

Run it in your project — Next.js, Nuxt, SvelteKit, Astro, React Router, SolidStart, React or Vue with Vite, Expo, Express, Hono, Fastify or NestJS:

**npm**

```bash
npx @noverax/create-courier
```

**pnpm**

```bash
pnpm create @noverax/courier
```

**yarn**

```bash
yarn create @noverax/courier
```

**bun**

```bash
bun create @noverax/courier
```

It shows the plan, and once you agree adds a typed example API, the route the browser calls it through, `courier.config.ts`, a mock and a page at `/courier`; installs the packages with your package manager; and type-checks what it added. Then:

```bash
npm run studio   # Courier Studio, and the mock API it runs
npm run dev      # your app: open /courier
```

The page lists the users your API answers with — from the mock until `API_URL` names your backend — and creates one from a form; every call shows in Studio. For a new project, name a folder and the framework: `npx @noverax/create-courier shop --framework next`. [Create Courier](/docs/create-courier) has every option, and what it changes in each framework.

## By hand

The same setup, step by step.

### Step 1: Define your API

Create a file with your API definition using `courier`, `defineGroup`, and `defineApi`:

```ts title="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",
  // A page may call these through your server's proxy
  expose: true,
  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>(),
    update: endpoint.put(":id").variables<{ id: string; name: string }>().response<User>(),
    remove: endpoint.del(":id").variables<{ id: string }>().response<null>(),
  },
})

export const api = defineApi({
  baseURL: process.env.API_URL!,
  groups: { users },
})
```

> **Path parameters**
>
> The `:id` syntax automatically extracts `id` from variables and inserts it into the URL. Remaining variables become query params (GET) or body (POST/PUT/PATCH).

### Step 2: Set up the caller

Choose the mode that fits your use case:

**Direct mode** — call your backend from any runtime:

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

export const caller = createCaller(api)
```

**Proxy mode** — browser requests go through your server (hides API keys, bypasses CORS). The proxy serves only the endpoints marked `expose`, as the `users` group is above, and answers any other as a name that does not exist. List your APIs in `courier.config.ts`:

```ts title="courier.config.ts"
import { defineConfig } from "@noverax/courier/config"
import { api } from "./api/sources"

export default defineConfig({ sources: { api } })
```

From the marks, Courier writes the routes a page's caller takes — each endpoint a page may call, and how it reaches it — into `courier.routes.ts` beside the config. Studio writes them while it runs; `npx @noverax/courier-cli routes` writes them now, and with `--check` fails a CI job when they are out of date. The browser's caller takes them in place of the API:

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

export const caller = createProxyCaller(apiRoutes)
```

The caller sends each call to `/api/request` on your server, which runs it with the sources registered there — your [framework guide](/docs/frameworks/nextjs) sets up that route and the registration. See [Exposing Endpoints](/docs/proxy-mode/expose) for what the browser learns and what it never does.

**React Query** — hooks with full type inference:

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

export const q = createProxyQueryCaller(apiRoutes)
```

> **Use both modes together**
>
> Most apps use proxy/React Query for client-side and direct mode for server-side. Both share the same API definitions.

### Step 3: Use it

**With React Query:**

```tsx title="app/users/page.tsx"
"use client"
import { q } from "@/lib/query"

export default function UsersPage() {
  const { data: users, isLoading } = q.users.useQuery("getAll")
  //      ^? User[]

  const { mutate } = q.users.useMutation("create", {
    onSuccess: (user) => console.log("Created:", user.name),
    //                                           ^? string
  })

  if (isLoading) return <p>Loading...</p>

  return (
    <div>
      <ul>
        {users?.map((u) => <li key={u.id}>{u.name} — {u.email}</li>)}
      </ul>
      <button onClick={() => mutate({ name: "Jane", email: "jane@test.com" })}>
        Add User
      </button>
    </div>
  )
}
```

**Direct mode (server-side):**

```tsx title="app/dashboard/page.tsx"
import { caller } from "@/lib/caller"

export default async function DashboardPage() {
  const users = await caller.users.getAll()
  //    ^? User[]

  return <ul>{users.map((u) => <li key={u.id}>{u.name}</li>)}</ul>
}
```

**Vanilla TypeScript (no framework):**

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

const caller = createCaller(api)

const users = await caller.users.getAll()
const user = await caller.users.getById({ id: "1" })
```

## What's happening under the hood

When you call `caller.users.getById({ id: "5" })`:

1. Courier resolves the `users` group and `getById` method
2. The `:id` path parameter is extracted from variables → `GET /users/5`
3. Middleware chain runs (retry, auth, logging, etc.)
4. Schema validation runs (if configured)
5. The typed response flows back to your code

In proxy mode, step 2-5 happen on your server. The browser only sees `/api/request`.

## Next steps

- [Framework Guides](/docs/frameworks/nextjs) — complete setup for your framework
- [Middleware](/docs/middleware/overview) — add retry, auth, logging
- [Examples](/docs/examples/basic) — interactive demos
