Next.js
NextjsWebAdapter binds typed Route Handlers over @postel/http — inbound.<source>.post, bindJwks, and an admin router binding.
Next.js Route Handlers receive a Web Request and return a Response, so @postel/nextjs is a thin, typed facade over @postel/http: NextjsWebAdapter(postel) returns Route Handlers keyed by HTTP method — App Router route files export those directly.
Inbound webhook route
import { NextjsWebAdapter } from "@postel/nextjs";
import { postel } from "@/lib/postel"; // Postel({ inbound: { vendor: { verify: Secret(...) } } })
const nwa = NextjsWebAdapter(postel);
export const { POST } = nwa.inbound.vendor.post((result) => {
return Response.json({ ok: true, type: result.event.type });
});The source key is type-checked against the sources you configured, and result is typed to that source's schema output. On failure the gate short-circuits with the mapped HTTP status and your handler never runs; a non-PostelError bubbles as a 5xx. Use .on(method, handler, opts) to gate a body-bearing method other than POST (PUT | PATCH).
JWKS route
import { NextjsWebAdapter } from "@postel/nextjs";
import { postel } from "@/lib/postel";
export const { GET } = NextjsWebAdapter(postel).outbound.bindJwks();Admin route
import { NextjsWebAdapter } from "@postel/nextjs";
import { postel } from "@/lib/postel";
export const { GET, POST, PUT, PATCH, DELETE } = NextjsWebAdapter(postel).admin.bindAdminRoutes({
authorize: (req) => check(req),
});Low-level primitive
The facade is sugar over withWebhook(source, handler, opts?) — use it to gate a route file by hand without the facade:
import { withWebhook } from "@postel/nextjs";
import { postel } from "@/lib/postel";
export const POST = withWebhook(postel.inbound.vendor, (result) =>
Response.json({ ok: true, type: result.event.type }),
);