Build a webhook handler in Next.js
A Next.js App Router route handler that reads the raw request body (not the parsed JSON) so signature verification works, then acknowledges fast.
In the Next.js App Router, a webhook is just a POST route handler. The catch is
reading the body correctly: use await req.text() to get the raw string, not req.json(), or signature verification will fail.
A minimal route handler
Put the handler at app/api/webhooks/route.ts and export an async POST. Read the raw body once, verify it, acknowledge with 200, then process asynchronously. Reject bad
signatures with 401.
// app/api/webhooks/route.ts (App Router route handler)
import crypto from 'node:crypto';
const SECRET = process.env.WEBHOOK_SECRET!;
export async function POST(req: Request) {
const raw = await req.text(); // RAW body — not req.json()
const sig = req.headers.get('x-webhook-signature');
if (!verify(raw, sig)) {
return new Response('bad signature', { status: 401 });
}
// Kick off work without blocking the response.
queueMicrotask(() => process(JSON.parse(raw)));
return new Response('ok'); // fast 2xx
}
function verify(raw: string, sig: string | null) {
if (!sig) return false;
const expected = crypto.createHmac('sha256', SECRET).update(raw).digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(sig);
return a.length === b.length && crypto.timingSafeEqual(a, b);
} Notes
- The pattern mirrors the Node/Express receiver — same raw-body + constant-time-compare rules, different framework.
- For heavier work, push the job to a real queue instead of
queueMicrotaskso a crash doesn’t drop it.
Point the provider at a HookWatch endpoint and every delivery is captured. When a handler throws you can see the exact request and response and replay the delivery once it’s fixed.
Keep reading
Start debugging your webhooks.
Point one endpoint at HookWatch, capture a failure, and replay it once it’s fixed. Free during beta.