# Project Setup

Recommended file structure, patterns, and configuration for production apps.

## Recommended file structure

Keep your API definitions separate from your UI code. The structure is the same regardless of framework:

```text title="Project structure"
courier.config.ts         # your APIs, for your server, Studio and the MCP server
courier.routes.ts         # what a page may call: Courier writes it from the .expose() marks
src/
├── api/
│   ├── index.ts          # defineApi + sources export
│   ├── users.ts          # users group (defineGroup)
│   ├── posts.ts          # posts group
│   └── files.ts          # files group
├── lib/
│   ├── caller.ts         # createProxyCaller, built once
│   ├── query.ts          # createProxyQueryCaller, built once (React)
│   └── server-caller.ts  # createCaller for server code
├── app/                  # (Next.js) or pages/ (Nuxt) or routes/ (SvelteKit)
│   ├── api/
│   │   └── request/
│   │       └── route.ts  # proxy route handler
│   └── ...
└── server/               # (if using Express/Hono/Fastify separately)
    └── index.ts
```

## Source definitions

The `api/` directory contains your API structure. Each group lives in its own file:

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

const { endpoint } = courier()

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

export const users = defineGroup({
  prefix: "users",
  // A page may call these through the proxy; an endpoint no mark opens stays the server's
  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>(),
  },
})
```

The main file combines groups into an API source:

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

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

export { api }
```

> **One group per file**
>
> Keeping each group in its own file makes it easy to find and modify endpoints. Types stay co-located with the endpoints that use them.

## Caller setup

Create the appropriate caller for your use case, once, in a module of its own, and import it wherever a call is made — see [Singleton Pattern](/docs/advanced/singleton):

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

export const q = createProxyQueryCaller(apiRoutes)
// In components: q.users.useQuery("getAll"), q.users.useMutation("create")
```

```ts title="lib/caller.ts (Vanilla / Vue / Svelte)"
import { createProxyCaller } from "@noverax/courier"
import { apiRoutes } from "@/courier.routes"

export const caller = createProxyCaller(apiRoutes)
```

The browser's callers take the routes Courier writes from your marks, never the API itself — see [Exposing Endpoints](/docs/proxy-mode/expose).

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

export const caller = createCaller(api)
```

## Multiple backends

Define a separate `defineApi` for each backend service:

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

const mainApi = defineApi({
  baseURL: process.env.API_URL!,
  groups: { users, posts },
})

const authApi = defineApi({
  baseURL: process.env.AUTH_API_URL!,
  groups: { auth },
})

export { mainApi, authApi }
```

Create separate callers for each API, with full type inference:

```ts
const mainCaller = createCaller(mainApi)
const authCaller = createCaller(authApi)

const users = await mainCaller.users.getAll()
const token = await authCaller.auth.login({ email, password })
```

## Environment variables

Backend URLs should always be server-side environment variables:

```bash title=".env.local"
API_URL=http://localhost:4000
AUTH_API_URL=http://localhost:5000
```

> **Never expose API_URL to the client**
>
> In proxy mode, the browser only talks to `/api/request`. Your backend URL, API keys, and other secrets stay on the server.

## Next steps

- [Framework Guides](/docs/frameworks/nextjs) — complete setup for your framework
- [Examples](/docs/examples/basic) — interactive demos of every feature
