# Express



```ts title="lib/postel.ts"
import { Postel, Secret } from "@postel/core";
import { config } from "./config.js";

export const postel = Postel({
  inbound: {
    vendor: {
      verify: Secret(config.webhookSecret),
    },
  },
});
```

## Routing facade [#routing-facade]

`ExpressWebAdapter(postel, app)` registers gated routes by source key. Each route mounts `express.raw({ type: () => true })` ahead of the gate for you, so the verifier sees the exact received bytes — don't put `express.json()` in front. The verified result is on `req.postel`.

```ts title="app.ts"
import express from "express";
import { ExpressWebAdapter } from "@postel/express";
import { postel } from "./lib/postel";

const app = express();
const ewa = ExpressWebAdapter(postel, app);

ewa.inbound.vendor.post("/webhooks/vendor", (req, res) => {
  res.json({ ok: true, type: req.postel.event.type });
});
```

Source keys are type-checked. On failure the gate writes the mapped 4xx and your handler never runs; a non-`PostelError` is forwarded to `next(err)` (your error middleware / 500).

### Other methods [#other-methods]

`.post` is sugar for `.on("POST", …)`. For a provider that delivers over `PUT`/`PATCH`, bind the method explicitly — body-bearing verbs (`POST` | `PUT` | `PATCH`) only, since the gate verifies a signature over the body:

```ts
ewa.inbound.vendor.on("PUT", "/webhooks/vendor", (req, res) => {
  res.json({ ok: true });
});
```

## JWKS [#jwks]

```ts
ewa.outbound.bindJwks();                         // GET /.well-known/webhooks-keys
ewa.outbound.bindJwks("/keys", customProvider);  // custom route + provider
```

`bindJwks()` defaults the provider to `postel.outbound.keys.publicJwks()`; available when an `outbound` slot is configured.

## Admin [#admin]

```ts
ewa.admin.bindAdminRoutes("/admin", { authorize: (req) => checkAdminToken(req) });
```

## Low-level primitives [#low-level-primitives]

The facade wraps `withWebhook` / `verifyWebhook` (each returns `[express.raw(...), gate]`) and the `fetchToExpress` bridge:

```ts
import { verifyWebhook, fetchToExpress } from "@postel/express";
import { adminRouter } from "@postel/admin";

app.post("/webhooks/vendor", verifyWebhook(postel.inbound.vendor), (req, res) => res.send("ok"));
app.use("/admin", fetchToExpress(adminRouter(postel, { authorize: (req) => checkAdminToken(req) })));
```
