# Fastify

Fastify server adapter using courierPlugin for hosting the Courier proxy route.

The `@noverax/courier-fastify` adapter provides a Fastify plugin that registers the Courier proxy route.

## In one command

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

In a Fastify server it adds a plugin with the proxy route and a page at `/courier` — loaded by itself in an app fastify-cli made — with a typed example API and its mock. See [Create Courier](/docs/create-courier).

## Installation

```bash
npm install @noverax/courier @noverax/courier-fastify fastify @fastify/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 a Fastify server with the Courier plugin:

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

registerSources(sources)

const app = Fastify({ logger: true })

async function start() {
  await app.register(cors, {
    origin: "http://localhost:5173",
    credentials: true,
  })

  // Register the Courier proxy plugin
  await app.register(courierPlugin, {
    prefix: "/api/request",
  })

  await app.listen({ port: 3001 })
}

start()
```

The `courierPlugin` registers a route handler at the specified prefix that reads sources from the core global registry populated by `registerSources()` at module load.

## With existing Fastify app

Add the Courier plugin alongside your existing routes and plugins:

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

registerSources(sources)

const app = Fastify({ logger: true })

async function start() {
  await app.register(cors, {
    origin: "http://localhost:5173",
    credentials: true,
  })

  // Your existing routes
  app.get("/health", async () => {
    return { status: "ok" }
  })

  app.get("/api/stats", async () => {
    return { uptime: process.uptime() }
  })

  // Register Courier proxy plugin
  await app.register(courierPlugin, {
    prefix: "/api/request",
  })

  await app.listen({ port: 3001 })
}

start()
```

> **Plugin encapsulation**
>
> The `courierPlugin` follows Fastify's plugin system. It encapsulates its routes under the given prefix without interfering with your other routes or plugins.

## CORS configuration

For production, restrict the allowed origins:

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

registerSources(sources)

const app = Fastify({ logger: true })

async function start() {
  await app.register(cors, {
    origin: process.env.ALLOWED_ORIGINS?.split(",") ?? "http://localhost:5173",
    credentials: true,
  })

  await app.register(courierPlugin, {
    prefix: "/api/request",
  })

  await app.listen({
    port: Number(process.env.PORT) || 3001,
    host: "0.0.0.0",
  })
}

start()
```

> **Fastify performance**
>
> Fastify's schema-based validation and serialization make it one of the fastest Node.js frameworks. The Courier plugin benefits from Fastify's built-in JSON parsing, so no additional body parser setup is needed.

## 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="server.ts"
import { requestContext } from "@noverax/courier-fastify"

app.addHook("onRequest", requestContext())
```

Add the hook on the root instance, where it runs for every route whichever plugin registers it. The session cookies the API sets reach the browser on the reply, beside any your route sets, and the user's address follows the rule the proxy route follows. Fastify 4 and 5 are checked. See [User Sessions](/docs/server-side/session).

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

Wire your Fastify 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 Fastify from "fastify"
import { courierPlugin } from "@noverax/courier-fastify"

const app = Fastify()

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

  await app.register(courierPlugin, { prefix: "/api/request" })

  await app.listen({ port: 3001 })
}

start()
```

Alternatively, set `COURIER_APP_URL` instead of passing `appOrigin` explicitly:

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