Skip to content

Instantly share code, notes, and snippets.

@bmorrisondev
Created April 23, 2024 19:30
Show Gist options
  • Save bmorrisondev/ed422d06897e6e6aab8c15770cd561d5 to your computer and use it in GitHub Desktop.
Save bmorrisondev/ed422d06897e6e6aab8c15770cd561d5 to your computer and use it in GitHub Desktop.
import { WebhookEvent } from "@clerk/nextjs/server";
import { headers } from "next/headers";
import { NextRequest, NextResponse } from "next/server";
import { Webhook } from "svix";
export type WebhookRegistrationConfig = {
secret?: string
urlPrefix?: string
onUserCreated?: (event: WebhookEvent) => Promise<void | NextResponse>;
onUserUpdated?: (event: WebhookEvent) => Promise<void | NextResponse>;
}
export async function handleWebhooks(config: WebhookRegistrationConfig, req: NextRequest) {
// If the request is not a POST request or does not start with the webhook URL, return a 404
if (req.method !== 'POST' || !req.nextUrl.pathname.startsWith(config.urlPrefix ? config.urlPrefix : "/_clerkhook")) {
NextResponse.next()
return
}
const WEBHOOK_SECRET = config.secret ? config.secret : process.env.WEBHOOK_SECRET
if (!WEBHOOK_SECRET) {
throw new Error('Please add WEBHOOK_SECRET from Clerk Dashboard to .env or .env.local')
}
const headerPayload = headers();
const svix_id = headerPayload.get("svix-id");
const svix_timestamp = headerPayload.get("svix-timestamp");
const svix_signature = headerPayload.get("svix-signature");
if (!svix_id || !svix_timestamp || !svix_signature) {
return new Response('Error occurred -- no svix headers', {
status: 400
})
}
// Get the body
const payload = await req.json()
const body = JSON.stringify(payload);
const wh = new Webhook(WEBHOOK_SECRET);
let evt: WebhookEvent
try {
evt = wh.verify(body, {
"svix-id": svix_id,
"svix-timestamp": svix_timestamp,
"svix-signature": svix_signature,
}) as WebhookEvent
} catch (err) {
console.error('Error verifying webhook:', err);
return new Response('Error occured', {
status: 400
})
}
switch(evt.type) {
case 'user.created':
if (config.onUserCreated) {
return await _handler(evt, config.onUserCreated)
}
break;
case 'user.updated':
if (config.onUserUpdated) {
return await _handler(evt, config.onUserUpdated)
}
break;
}
// If we don't have a handler for the event, return a 404
return NextResponse.next()
}
async function _handler(event: WebhookEvent, callback: Function): Promise<NextResponse | Response> {
let response = await callback(event)
if(response != undefined) {
return response
} else {
return new Response('', { status: 200 })
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment