SDK Getting Started
Build type-safe webhook handlers with automatic signature verification using Better Webhook SDK packages.
SDK Getting Started
The Better Webhook SDK provides type-safe webhook handlers with automatic signature verification. It consists of:
- Provider packages — Type definitions and schemas for webhook sources (GitHub, Ragie)
- Adapter packages — Framework integrations (Next.js, Express, NestJS, GCP Cloud Functions)
- Core package — Base functionality for custom providers
Installation
Install a provider and an adapter for your framework:
bash npm install @better-webhook/github @better-webhook/nextjs
bash pnpm add @better-webhook/github @better-webhook/nextjs
bash yarn add @better-webhook/github @better-webhook/nextjs
bash npm install @better-webhook/github @better-webhook/express
bash pnpm add @better-webhook/github @better-webhook/express
bash yarn add @better-webhook/github @better-webhook/express
bash npm install @better-webhook/github @better-webhook/nestjs
bash pnpm add @better-webhook/github @better-webhook/nestjs
bash yarn add @better-webhook/github @better-webhook/nestjs
bash pnpm add @better-webhook/ragie @better-webhook/gcp-functions
bash yarn add @better-webhook/ragie @better-webhook/gcp-functions
Quick Example
import { github } from "@better-webhook/github";
import { toNextJS } from "@better-webhook/nextjs";
const webhook = github()
.event("push", async (payload) => {
// payload is fully typed!
console.log(`Push to ${payload.repository.name}`);
console.log(`${payload.commits.length} commits`);
})
.event("pull_request", async (payload) => {
if (payload.action === "opened") {
console.log(`New PR: ${payload.pull_request.title}`);
}
});
export const POST = toNextJS(webhook);import express from "express";
import { github } from "@better-webhook/github";
import { toExpress } from "@better-webhook/express";
const app = express();
const webhook = github()
.event("push", async (payload) => {
console.log(`Push to ${payload.repository.name}`);
});
// Important: use express.raw() for signature verification
app.post(
"/webhooks/github",
express.raw({ type: "application/json" }),
toExpress(webhook)
);
app.listen(3000);import { Controller, Post, Req, Res } from "@nestjs/common";
import { Request, Response } from "express";
import { github } from "@better-webhook/github";
import { toNestJS } from "@better-webhook/nestjs";
@Controller("webhooks")
export class WebhooksController {
private webhook = github()
.event("push", async (payload) => {
console.log(`Push to ${payload.repository.name}`);
});
@Post("github")
async handleGitHub(@Req() req: Request, @Res() res: Response) {
const result = await toNestJS(this.webhook)(req);
return res.status(result.statusCode).json(result.body);
}
}import { http } from "@google-cloud/functions-framework";
import { ragie } from "@better-webhook/ragie";
import { toGCPFunction } from "@better-webhook/gcp-functions";
const webhook = ragie()
.event("document_status_updated", async (payload) => {
// payload is fully typed!
console.log(`Document ${payload.document_id} is now ${payload.status}`);
})
.event("connection_sync_finished", async (payload) => {
console.log(`Sync ${payload.sync_id} completed`);
});
http("webhookHandler", toGCPFunction(webhook));How It Works
Create a Webhook Builder
Use a provider function (like github()) to create a webhook builder:
import { github } from "@better-webhook/github";
const webhook = github();Register Event Handlers
Chain .event() calls to register handlers for specific event types:
const webhook = github()
.event("push", async (payload) => {
// Handle push events
})
.event("issues", async (payload) => {
// Handle issues events
});Each handler receives a fully typed payload with autocomplete support.
Convert to Framework Handler
Use an adapter to convert the webhook builder to your framework's handler format:
// Next.js
export const POST = toNextJS(webhook);
// Express
app.post("/webhooks/github", express.raw({ type: "application/json" }), toExpress(webhook));
// NestJS
const result = await toNestJS(this.webhook)(req);
// GCP Cloud Functions
http("webhookHandler", toGCPFunction(webhook));Signature Verification
Signature verification happens automatically when you provide a secret. The SDK looks for secrets in this order:
- Adapter options — Pass
secretto the adapter function - Provider options — Pass
secretwhen creating the provider - Environment variables — Automatically checks
GITHUB_WEBHOOK_SECRET,RAGIE_WEBHOOK_SECRET, etc.
// Option 1: Adapter options
export const POST = toNextJS(webhook, {
secret: process.env.GITHUB_WEBHOOK_SECRET,
});
// Option 2: Provider options
const webhook = github({
secret: process.env.GITHUB_WEBHOOK_SECRET,
});
// Option 3: Environment variable (automatic)
// Just set GITHUB_WEBHOOK_SECRET in your environmentAlways configure signature verification in production. Without it, anyone can send fake webhooks to your endpoint.
Error Handling
Register error handlers to catch validation and handler errors:
const webhook = github()
.event("push", async (payload) => {
// Your handler
})
.onError((error, context) => {
console.error(`Error in ${context.eventType}:`, error);
// Log to monitoring service, etc.
})
.onVerificationFailed((reason, headers) => {
console.warn("Signature verification failed:", reason);
// Alert on potential attacks
});Observability
Add metrics, logging, and tracing to your webhook handlers with the built-in observer API:
import { github } from "@better-webhook/github";
import { createWebhookStats } from "@better-webhook/core";
import { toNextJS } from "@better-webhook/nextjs";
// Built-in stats collector
const stats = createWebhookStats();
const webhook = github()
.observe(stats.observer)
.event("push", async (payload) => {
console.log(`Push to ${payload.repository.name}`);
});
export const POST = toNextJS(webhook);
// Access stats anytime
console.log(stats.snapshot());
// { totalRequests: 150, successCount: 145, errorCount: 5, ... }Custom Observers
Create custom observers to integrate with your metrics infrastructure:
import { type WebhookObserver } from "@better-webhook/core";
const metricsObserver: WebhookObserver = {
onCompleted: (event) => {
metrics.histogram("webhook_duration_ms", event.durationMs, {
provider: event.provider,
eventType: event.eventType,
status: String(event.status),
});
},
onHandlerFailed: (event) => {
logger.error("Webhook handler failed", {
error: event.error.message,
provider: event.provider,
eventType: event.eventType,
});
},
};
const webhook = github().observe(metricsObserver).event("push", handler);You can also add observers at the adapter level:
export const POST = toNextJS(webhook, {
observer: metricsObserver,
});