Framework adapters
Mount the same handler on Next.js, Hono, Express, bare Node — or anything that speaks Request.
The handler’s native interface is Web-standard: handler.fetch is a
(request: Request) => Promise<Response> function. If your platform speaks
Request — Next.js App Router, Cloudflare Workers, Deno, Bun, Remix — you
need no adapter at all:
export const POST = handler.fetch
Adapters exist for the platforms that don’t, each on its own subpath so nothing you skip is bundled.
Next.js App Router — webhooks-sdk/next
import { toNextRoute } from 'webhooks-sdk/next'
export const { POST } = toNextRoute(handler)
toNextRoute also returns GET and PUT — export those too for providers
that confirm a new endpoint with a challenge query parameter:
export const { POST, GET } = toNextRoute(handler)
For the Pages Router, disable the body parser and use the Node adapter:
import { toNodeHandler } from 'webhooks-sdk/node'
export const config = { api: { bodyParser: false } }
export default toNodeHandler(handler)
Hono — webhooks-sdk/hono
import { toHonoHandler } from 'webhooks-sdk/hono'
app.post('/webhooks/stripe', toHonoHandler(handler))
Works wherever Hono does — Workers, Deno, Bun, Node. The adapter is structurally typed against the Hono context, so Hono is not a dependency of the SDK.
Express — webhooks-sdk/express
Express is where the raw body bites. Two ways to get it right:
Mount the webhook route with a raw body before any JSON parser:
import express from 'express'
import { toExpressHandler } from 'webhooks-sdk/express'
app.post('/webhooks/stripe', express.raw({ type: '*/*' }), toExpressHandler(handler))
app.use(express.json())If you can’t reorder middleware, capture the bytes before the global parser consumes them:
import { captureRawBody, toExpressHandler } from 'webhooks-sdk/express'
app.use(express.json({ verify: captureRawBody }))
app.post('/webhooks/stripe', toExpressHandler(handler))The adapter picks up the untouched bytes from either path — and handles the detail that Node pools Buffers, so a captured body is often a view into a larger allocation.
Bare Node — webhooks-sdk/node
For node:http servers and anything built on them:
import { createServer } from 'node:http'
import { toNodeHandler } from 'webhooks-sdk/node'
const server = createServer(toNodeHandler(handler))
The module also exports the building blocks — readRawBody(request) and
fromNodeRequest(request, rawBody?) — if you’re wiring into a framework
with its own request type. Everything is structurally typed, so the SDK
never depends on @types/node.
Response behavior
All adapters produce the same responses handler.fetch would: handshake
responses pass through verbatim, errors return their
status and JSON body, everything else returns 200.