# Server-Side Usage

Call your backend directly from any server runtime — no proxy hop, no client bundle, zero overhead.

## What is direct mode?

In **direct mode**, your server-side code calls the backend directly without routing through a proxy handler. The request flow is straightforward:

```
createCaller → executeRequest → HttpClient → fetch → backend
```

There is no intermediate HTTP hop, no serialization through a proxy endpoint, and no exposure to the client bundle. Your backend URL, API keys, and other secrets remain on the server.

> **Same sources, different transport**
>
> Direct mode uses the same `defineApi` and `defineGroup` definitions as proxy mode. You define your API structure once and use it from both client and server code.

## When to use direct mode

Use direct mode whenever your code runs on the server:

- **React Server Components (RSC)** — fetch data at render time with zero client JavaScript
- **Server Actions** — handle form submissions and mutations without writing API routes
- **API Routes** — when your route handler needs to aggregate data from your backend
- **Any server runtime** — Node.js, Deno, Bun, or edge runtimes with fetch support

## Benefits over proxy mode

- **Lower latency** — eliminates the extra HTTP hop through the proxy endpoint
- **Server-only secrets** — backend URLs and API keys never appear in the client bundle
- **No client JavaScript** — RSC data fetching adds zero bytes to the browser bundle
- **Simpler error handling** — errors stay on the server, no need to serialize them for the client
- **Universal** — works in any runtime with native `fetch` (Node.js, Deno, Bun, edge)

## Proxy vs Direct mode

| Feature | Proxy Mode | Direct Mode |
| --- | --- | --- |
| Execution | Browser -> proxy handler -> backend | Server -> backend |
| Latency | Extra HTTP hop | Single hop |
| Client bundle | Includes request code | Zero bytes |
| React Query | Built-in hooks | Not needed (async/await) |
| The user's session | The API's session, from the browser's request | The API's session, from the request being served |
| Use case | Client components | RSC, Server Actions, API routes |
| Requires proxy handler | Yes | No |

## Quick setup

Create a caller by passing your sources to `createCaller`:

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

export const caller = createCaller(api)
```

## Usage in Server Components

Call the caller in any `async` server component. The data is fetched at render time on the server:

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

export default async function DashboardPage() {
  const users = await caller.users.getAll({ page: 1, limit: 10 })

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

## Usage in Server Actions

Server Actions use the same caller. A write made from the application's own form carries the user's session, and the double-submit token the API asks for:

```ts title="app/actions/users.ts"
"use server"

import { revalidatePath } from "next/cache"
import { server } from "@/lib/api.server"

export async function createUser(data: { name: string; email: string }) {
  const user = await server.users.create(data)

  revalidatePath("/dashboard")
  return user
}
```

## The user's session

The server's own `fetch` carries none of the user's cookies. An API that keeps a session describes it with `session` in `defineApi`, and a call made while the server serves a user carries that user's session from the request being served — the cookies the API keeps it in, and nothing else of the application's:

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

export const server = createServerCaller(api)
```

```ts title="app/profile/page.tsx"
import { server } from "@/lib/api.server"

export default async function ProfilePage() {
  const profile = await server.users.me()

  return <h1>Welcome, {profile.name}</h1>
}
```

[User Sessions](/docs/server-side/session) covers the options, the setup for each framework, the cookies the API sets, and the writes another site sets off.

> **Header whitelist**
>
> Headers read from a context — `createCaller(api, { context: { req } })` — are forwarded only when they are safe: `authorization`, `user-agent`, `referer`, `origin` and `x-request-id`, plus the names a source lists in `allowedHeaders` or an endpoint in `.headers()` — `cookie` among them when the user's session should travel. An `Authorization` the browser sent by itself, in `Basic`, `Digest`, `Negotiate` or `NTLM`, goes on only where `authorization` is named that way. A client address is not copied from the incoming request; `X-Forwarded-For` is written from the connection and the chain the proxies in front recorded — see [The user's address](/docs/deployment/overview#the-users-address). A header the API sets in its `headers` or its session is sent in place of the request's copy. Headers passed to `.with({ headers })` and custom headers from `defaults.headers` are always included, except those describing the incoming connection, such as `host`, `connection` and `content-length`.

## Security considerations

Called from server code, direct mode keeps everything on the server:

- Your backend URL (`API_URL`) is never sent to the browser
- API keys and tokens in `defaults.headers` stay server-side
- Source code for `createCaller` is tree-shaken from the client bundle
- Error details (stack traces, internal URLs) never leak to the client

> **Don't import caller in client components**
>
> If you import your caller in a `"use client"` file, your bundler will try to include your source definitions (including `process.env.API_URL`) in the client. Keep caller imports in server-only files.

## Next Steps

- [createCaller](/docs/server-side/caller) — full API reference, defaults, and advanced usage
- [Server Actions](/docs/server-side/actions) — type-safe mutations with `createAction`
- [User Sessions](/docs/server-side/session) — the user's session, carried to the API
- [Proxy Mode Overview](/docs/proxy-mode/overview) — client-side requests through the proxy handler
