# Express

> Mount the pipedog WebRTC offer endpoint in an Express 5 app.

Source: https://polvallverdu.github.io/pipedog/docs/frameworks/express/



Express 5 mounts the offer through `nodeRequestListener`, which turns a fetch handler into a Node
request listener. This snippet mounts only the offer.

## Install [#install]

```bash
bun add @pipedog/core @pipedog/asyncio @pipedog/transport-smallwebrtc @pipedog/transport-websocket @pipedog/serializer-protobuf @pipedog/openai @pipedog/audio-silero @pipedog/audio-smart-turn express
```

`@pipedog/audio-smart-turn` isn't imported anywhere: the default turn strategies load it. To run
the snippet from a checkout instead, see [Installation](https://polvallverdu.github.io/pipedog/docs/installation/#from-source).

## Files [#files]

The endpoints come from the shared module on the [overview](https://polvallverdu.github.io/pipedog/docs/frameworks/#the-two-endpoints).
`voice.ts` re-exports it:

```ts title="voice.ts"
export { offer, runSocketAgent, socket } from "../shared/voice.ts";

```

```ts title="server.ts"
import { nodeRequestListener } from "@pipedog/transport-smallwebrtc/http";
import express, { type Express } from "express";
import { offer } from "./voice.ts";

const app: Express = express();

// Don't add express.json(): the offer handler reads the body itself.
app.post("/api/offer", nodeRequestListener(offer));

export default app;

// `node server.ts` starts it on port 3000.
if (import.meta.main) {
  app.listen(3000);
}

```

Don't add `express.json()` in front of the route: the offer reads the body itself, and a body parser
would consume it first.

## Run it [#run-it]

```bash
OPENAI_API_KEY=sk-... node server.ts
```

It listens on port 3000. `bun server.ts` works too. Node runs the TypeScript with its built-in type
stripping, in a workspace; see [Runtimes](https://polvallverdu.github.io/pipedog/docs/guides/runtimes.md).

## Deploy [#deploy]

Run it as a long-lived Node or Bun process on a host that allows UDP traffic, for the offer's media.

## WebSocket [#websocket]

This snippet mounts only the offer. To serve WebSocket sessions too, serve `socket` on a second
port with `serveFetch(socket, { port })` from `@pipedog/transport-websocket/serve`, which upgrades
WebSockets on Node (with the `ws` package), Bun and Deno, or run a [Bun](https://polvallverdu.github.io/pipedog/docs/frameworks/bun.md) or
[Deno](https://polvallverdu.github.io/pipedog/docs/frameworks/deno.md) server for it. See [WebSocket](https://polvallverdu.github.io/pipedog/docs/transports/websocket.md).

## Authentication [#authentication]

The route has no auth. Wrap `offer` with a session check before `nodeRequestListener`, or add an
Express middleware in front of the route, as in [Authentication](https://polvallverdu.github.io/pipedog/docs/frameworks/#authentication).
