# Express

Express server adapter using courierHandler for hosting the Courier proxy route.

The `@noverax/courier-express` adapter provides a handler for Express that serves as the Courier proxy route.

## In one command

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

In an Express server it adds a router with the proxy route and a page at `/courier`, mounted on your app before its body parsers, with a typed example API and its mock. See [Create Courier](/docs/create-courier).

## Installation

```bash
npm install @noverax/courier @noverax/courier-express express cors
npm install -D @types/express @types/cors
```

## Define your API sources

```ts title="lib/sources.ts"
import { courier, defineApi, defineGroup } from "@noverax/courier/define"

const { endpoint } = courier()

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

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: number }>().response<User>(),
    create: endpoint.post("").variables<{ name: string; email: string }>().response<User>(),
    remove: endpoint.del(":id").variables<{ id: number }>().response<null>(),
  },
})

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

export const sources = { myApi: api }
```

## Setup

Create an Express server with the Courier handler:

```ts title="server.ts"
import express from "express"
import cors from "cors"
import { registerSources } from "@noverax/courier"
import { courierHandler } from "@noverax/courier-express"
import { sources } from "./lib/sources"

registerSources(sources)

const app = express()

app.use(cors({ origin: "http://localhost:5173", credentials: true }))

// No body parser before this line: Courier reads the raw body itself, and a
// global `express.json()` would consume it first.

// Mount the Courier proxy route
app.all("/api/request", courierHandler())

app.listen(3001, () => {
  console.log("Server running on http://localhost:3001")
})
```

`registerSources(sources)` populates the in-process registry that `courierHandler()` reads at request time. The adapter is zero-arg — pass only behavioral options like `allowedOrigins` or `sseHeaders`. When you wire Studio on top (see below), `attachRuntime()` also loads `courier.config.ts` and registers its sources in development; keep your own `registerSources()` call, which is what registers them in production, where the runtime is not attached.

## With existing Express app

If you already have an Express server, add the Courier handler alongside your existing routes:

```ts title="server.ts"
import express from "express"
import cors from "cors"
import { registerSources } from "@noverax/courier"
import { courierHandler } from "@noverax/courier-express"
import { sources } from "./lib/sources"

registerSources(sources)

const app = express()

app.use(cors({ origin: "http://localhost:5173", credentials: true }))

// Courier reads the raw body itself, so the proxy route must come before any
// body parser — or the parser must be scoped to the routes that need it.
app.all("/api/request", courierHandler())

// Your existing routes, with body parsing where you want it
app.use(express.json())

app.get("/health", (req, res) => {
  res.json({ status: "ok" })
})

app.get("/api/stats", (req, res) => {
  res.json({ uptime: process.uptime() })
})

app.listen(3001)
```

> **Body parsing**
>
> A body parser mounted with a bare `app.use(express.json())` applies to **every** route, `/api/request` included. The Courier handler reads the raw request body itself, so a parser that ran first leaves it nothing — the request fails with `REQUEST_BODY_ALREADY_READ`, which names this as the cause.
>
> Either register the proxy route before the parser, as above, or scope the parser to the paths that need it: `app.use("/api/users", express.json())`. The same applies to `express.urlencoded()` and `multer`.

## CORS configuration

For production, restrict the allowed origins:

```ts title="server.ts"
import express from "express"
import cors from "cors"
import { registerSources } from "@noverax/courier"
import { courierHandler } from "@noverax/courier-express"
import { sources } from "./lib/sources"

registerSources(sources)

const app = express()

app.use(
  cors({
    origin: process.env.ALLOWED_ORIGINS?.split(",") ?? "http://localhost:5173",
    credentials: true,
  })
)

// Before any body parser — Courier reads the raw body itself.
app.all("/api/request", courierHandler())

app.listen(Number(process.env.PORT) || 3001)
```

> **Credentials**
>
> Set `credentials: true` in your CORS config if your backend uses cookies for authentication. The Courier proxy route sends your backend the session its source describes, and hands the browser the session cookies the backend sets — see [User Sessions](/docs/server-side/session).

## The user's session

An API that keeps a `session` gets the user's session with every call a route, a service or a step it awaits makes, once the request is served with it at hand:

```ts title="app.ts"
import { requestContext } from "@noverax/courier-express"

app.use(requestContext())
```

Mount it before the routes that call APIs. The session cookies the API sets reach the browser on the response, and the user's address follows the rule the proxy route follows. See [User Sessions](/docs/server-side/session).

## Studio integration & `appOrigin` <a id="studio-app-origin"></a>

Wire your Express server into Studio with a single `attachRuntime()` call. In development it loads `courier.config.ts` from disk, registers its sources, and opens the connection to Studio; in production the app's own `registerSources()` call registers them. Studio's Playground sends its proxy calls to the app's origin, which in development the app learns from the server itself — the socket the first request to a Courier route arrived on — so open a page that calls your API before the first Playground call. Where Studio cannot see the server, set `courier.config.ts.runtime.appOrigin` or the `COURIER_APP_URL` env var — see [How the app learns its origin](/docs/api/config#app-origin).

```ts title="server.ts"
import express from "express"
import { courierHandler } from "@noverax/courier-express"

if (process.env.NODE_ENV !== "production") {
  const { attachRuntime } = await import("@noverax/courier-studio-core/runtime")
  await attachRuntime()
}

const app = express()
app.all("/api/request", courierHandler())

app.listen(3001)
```

Alternatively, set `COURIER_APP_URL` instead of pinning `appOrigin` in the config:

```bash
COURIER_APP_URL=http://localhost:3001 node server.js
```

> **No magic default**
>
> There is no `localhost:3001` auto-fallback. Until the config, the environment or the server itself names the origin, a Playground call in proxy mode throws `ConfigurationError("APP_ORIGIN_NOT_CONFIGURED")` with a pointer to [how the app learns its origin](/docs/api/config#app-origin). The correct value depends on your runtime — custom port, Docker host, HTTPS, reverse proxy — and a wrong guess would silently route requests to nowhere.

## Client-side setup

On the client, point your caller to this server's proxy URL. Choose the guide for your frontend:

- [React (Vite / CRA)](/docs/frameworks/react) — `createProxyQueryCaller` with React Query hooks
- [Vue](/docs/frameworks/vue) — `createProxyCaller` with composables
- [Next.js](/docs/frameworks/nextjs) — full-stack with `apiRoute` and `createProxyQueryCaller`
- [Nuxt](/docs/frameworks/nuxt) — full-stack with `courierHandler` and `createProxyCaller`

The caller takes the routes Courier writes into `courier.routes.ts` from the endpoints your sources mark `.expose()`, never the definition itself — see [Exposing Endpoints](/docs/proxy-mode/expose):

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

export const caller = createProxyCaller(myApiRoutes, {
  proxyPath: { http: "http://localhost:3001/api/request" },
})
```
