# NestJS

NestJS adapter: courierMiddleware serves the Courier proxy route, and requestContext carries the user's session in server code.

The `@noverax/courier-nestjs` adapter provides a middleware that integrates Courier into your NestJS application using the standard `MiddlewareConsumer`.

## In one command

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

In a NestJS app it adds a module with the proxy route and a page at `/courier`, imported into your root module, with a typed example API and its mock. See [Create Courier](/docs/create-courier).

## Installation

```bash
npm install @noverax/courier @noverax/courier-nestjs
```

## Define your API sources

```ts title="src/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

Apply the `courierMiddleware` in your root `AppModule` using the `MiddlewareConsumer`:

```ts title="src/app.module.ts"
import { Module, NestModule, MiddlewareConsumer } from "@nestjs/common"
import { registerSources } from "@noverax/courier"
import { courierMiddleware } from "@noverax/courier-nestjs"
import { sources } from "./lib/sources"

registerSources(sources)

@Module({
  imports: [],
  controllers: [],
  providers: [],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(courierMiddleware())
      .forRoutes("api/request")
  }
}
```

The `courierMiddleware` function returns a NestJS-compatible middleware that reads sources from the core global registry populated by `registerSources()` at module load.

## Request bodies

`NestFactory.create` parses JSON bodies for the whole application before any middleware runs, so a proxied JSON call reaches `courierMiddleware` already parsed. The middleware takes the body from Nest's parser, which stays on for your own controllers' `@Body()`. Create the app with `rawBody: true` and the proxy forwards the exact bytes the client sent; without it, the parsed JSON is serialized again, which gives the same value.

Nest's JSON parser accepts 100 KB by default — less than the proxy's own 1 MB `maxBodyBytes` — and answers a larger call with its own 413. Raise it to match:

```ts title="src/main.ts"
import { NestFactory } from "@nestjs/core"
import type { NestExpressApplication } from "@nestjs/platform-express"
import { AppModule } from "./app.module"

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule, { rawBody: true })
  app.useBodyParser("json", { limit: "1mb" })
  await app.listen(3001)
}

bootstrap()
```

Uploads (`multipart/form-data`) are not parsed by Nest and reach the proxy as a stream, up to its `maxUploadBytes` — 10 MB unless you pass another to `courierMiddleware`.

## With CORS

Enable CORS in your `main.ts` bootstrap:

```ts title="src/main.ts"
import { NestFactory } from "@nestjs/core"
import { AppModule } from "./app.module"

async function bootstrap() {
  const app = await NestFactory.create(AppModule)

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

  await app.listen(3001)
}

bootstrap()
```

## With existing modules

The Courier middleware sits alongside your existing NestJS controllers and modules:

```ts title="src/app.module.ts"
import { Module, NestModule, MiddlewareConsumer } from "@nestjs/common"
import { registerSources } from "@noverax/courier"
import { courierMiddleware } from "@noverax/courier-nestjs"
import { sources } from "./lib/sources"
import { HealthModule } from "./health/health.module"
import { StatsModule } from "./stats/stats.module"

registerSources(sources)

@Module({
  imports: [HealthModule, StatsModule],
  controllers: [],
  providers: [],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(courierMiddleware())
      .forRoutes("api/request")
  }
}
```

> **Middleware scope**
>
> The `forRoutes("api/request")` call limits the middleware to the proxy path only. It will not interfere with your other controllers or routes.

## Using with a dedicated module

For larger applications, you can isolate the Courier setup into its own module:

```ts title="src/courier/courier.module.ts"
import { Module, NestModule, MiddlewareConsumer } from "@nestjs/common"
import { registerSources } from "@noverax/courier"
import { courierMiddleware } from "@noverax/courier-nestjs"
import { sources } from "../lib/sources"

registerSources(sources)

@Module({})
export class CourierModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(courierMiddleware())
      .forRoutes("api/request")
  }
}
```

```ts title="src/app.module.ts"
import { Module } from "@nestjs/common"
import { CourierModule } from "./courier/courier.module"
import { HealthModule } from "./health/health.module"

@Module({
  imports: [CourierModule, HealthModule],
})
export class AppModule {}
```

> **Guards and interceptors**
>
> NestJS guards and interceptors applied globally or to the `api/request` route will execute before the Courier middleware. This lets you add authentication or logging on top of the proxy route using standard NestJS patterns.

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

Wire your NestJS app 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).

The natural place to call `attachRuntime` is in `main.ts`, alongside `bootstrap()`:

```ts title="src/main.ts"
import { NestFactory } from "@nestjs/core"
import { AppModule } from "./app.module"

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

  const app = await NestFactory.create(AppModule)
  await app.listen(3001)
}

bootstrap()
```

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

```bash
COURIER_APP_URL=http://localhost:3001 nest start
```

> **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.

## The user's session

An API that keeps a `session` gets the user's session with every call a guard, an interceptor, a pipe, a controller, a service or a step it awaits makes, once the request is served with it at hand. Mount the middleware as the application is created:

```ts title="src/main.ts"
import { NestFactory } from "@nestjs/core"
import { requestContext } from "@noverax/courier-nestjs"
import { AppModule } from "./app.module"

const app = await NestFactory.create(AppModule)
app.use(requestContext())
await app.listen(3000)
```

It runs before every module's own middleware, on the Express platform and on the Fastify platform alike. The session cookies the API sets reach the browser on the response, beside the ones the application sets — with `res.cookie()`, a `@Header()`, or a Fastify reply's headers. The user's address is taken from the socket, after the chain a proxy on a private network recorded. The request is kept in `AsyncLocalStorage`.

NestJS 10, 11 and 12 are checked on the Express platform, and 10 and 12 on the Fastify platform. See [User Sessions](/docs/server-side/session).

## 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" },
})
```
