Use case
Webhook handlers, without the server.
Every service you integrate with — Stripe, GitHub, Shopify, Slack, Linear, Zapier — wants an HTTPS URL to POST to. nvoke gives you that URL in seconds, with logs, retries, and a real editor.
import crypto from "node:crypto";
export default async function (req) {
const sig = req.headers["x-hub-signature-256"];
const expected =
"sha256=" +
crypto
.createHmac("sha256", process.env.WEBHOOK_SECRET)
.update(req.rawBody)
.digest("hex");
if (sig !== expected) return new Response("bad sig", { status: 401 });
const event = req.body;
if (event.action === "opened") {
await notifySlack(`New issue: ${event.issue.title}`);
}
return { ok: true };
}Why webhooks are the best case for nvoke
A webhook handler is the platonic ideal of a nvoke function. The shape of the work is receive a POST, do something, return quickly. There is no long-running state, no user session to track, no view to render. You want the code deployed somewhere with a URL you can paste into a webhook field, and you want to see logs when it fires.
The alternative is usually running a small Express app on a VPS, a Next.js route on Vercel, or a Lambda behind API Gateway. All of those work. All of them also make you carry a lot of infrastructure for what is usually fewer than fifty lines of code.
Signature verification
Every serious webhook sender signs the request. Stripe uses HMAC-SHA256 over the body; GitHub does the same with a different header; Shopify uses base64 HMAC. The pattern is the same: read the signature header, compute an HMAC over the raw body with your shared secret, compare with crypto.timingSafeEqual.
nvoke exposes req.rawBody as a Buffer so you can verify without worrying about JSON round-trip mutations. The parsed body is on req.body as usual.
Responding to retries
Webhook senders retry on non-2xx responses, usually with exponential backoff. That means two things for your handler. First: return fast. If you need to do heavy work, return 200 immediately and do the work out of band (a scheduled nvoke function polling a queue is a fine pattern). Second: be idempotent. The same event may arrive twice, and your function should not double-charge a customer or double-send a notification.
Signed body access
req.rawBody gives you the untouched request bytes. Verify the signature against your webhook secret before trusting anything.
Public endpoints
Mark the function as public so the sender does not need to attach an API key. Auth is your responsibility and lives in the function.
Retry-friendly logs
If the sender retries on 5xx, every retry shows up as a separate invocation. Replay any of them to reproduce a failure.
Ship a webhook handler today.
Create a function, copy the URL, paste it into the sender's dashboard. Done.