I'm trying to sync my Clerk data to my database in my Next js 13 project. My webhooks are publicly exposed with Ngrok. Here's my code:
import { IncomingHttpHeaders } from "http";
import { headers } from "next/headers";
import { NextResponse } from "next/server";
import { Webhook, WebhookRequiredHeaders } from "svix";
const webhookSecret = process.env.WEBHOOK_SECRET || "";
async function handler(request: Request) {
console.log(await request.json())
const payload = await request.json();
const headersList = headers();
const heads = {
"svix-id": headersList.get("svix-id"),
"svix-timestamp": headersList.get("svix-timestamp"),
"svix-signature": headersList.get("svix-signature"),
};
const wh = new Webhook(webhookSecret);
let evt: Event | null = null;
try {
evt = wh.verify(
JSON.stringify(payload),
heads as IncomingHttpHeaders & WebhookRequiredHeaders
) as Event;
} catch (err) {
console.error((err as Error).message);
return NextResponse.json({}, { status: 400 });
}
const eventType: EventType = evt.type;
if (eventType === "user.created" || eventType === "user.updated") {
const { id, ...attributes } = evt.data;
console.log(attributes)
}
}
type EventType = "user.created" | "user.updated" | "*";
type Event = {
data: Record<string, string | number>;
object: "event";
type: EventType;
};
export const GET = handler;
export const POST = handler;
export const PUT = handler;
This code should do the following:
/api/webhooks/user
However, only step 1 is working as far as I can tell. In my Clerk dashboard I get an error:
{
"message": "src property must be a valid json object"
}
With the following code I'm still getting the same error:
import { Webhook, WebhookRequiredHeaders } from "svix";
const webhookSecret = process.env.WEBHOOK_SECRET || "";
async function handler(request: Request) {
const svix_id = request.headers.get("svix-id") ?? "";
const svix_timestamp = request.headers.get("svix-timestamp") ?? "";
const svix_signature = request.headers.get("svix-signature") ?? "";
const body = await request.text(); // This get's the raw body as a string
const sivx = new Webhook("your_secret_key_here");
const payload = sivx.verify(body, {
"svix-id": svix_id,
"svix-timestamp": svix_timestamp,
"svix-signature": svix_signature,
});
console.log(payload)
}
export const GET = handler;
export const POST = handler;
export const PUT = handler;
Where am I going wrong?
You need to add your webhook endpoint to your ignoredRoutes in middleware.ts:
export default authMiddleware({
publicRoutes: ['/'],
ignoredRoutes: ['/api/{routeName}'],
});
There's annoyingly little documentation to highlight this though.