Skip to content
Webhooks SDK
Esc
navigateopen⌘Jpreview
On this page

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.

Was this page helpful?