4

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:

  1. Create an API route under /api/webhooks/user
  2. Fetch the payload and headers
  3. Validate this info with Svix
  4. Console the information

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"
}

Edit:

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?

Ethan
  • 118
  • 1
  • 13

2 Answers2

2

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.

0

I would first log the raw request:

const rawBody = await request.text();
console.log(rawBody);
const payload = JSON.parse(rawBody);

You are using request.json() to parse the incoming request body. This function returns a Promise that resolves with the result of parsing the body text as JSON.
If the incoming body text is not valid JSON, this could be the source of your error.

For example, "How to access request body in nextjs 13.2 route handler" uses const body = await req.text();:

  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 gets the raw body as a string

    const sivx = new Webhook(webhookSecret);

    try {
        const payload = sivx.verify(body, {
            "svix-id": svix_id,
            "svix-timestamp": svix_timestamp,
            "svix-signature": svix_signature,
        });

        console.log(payload);
    } catch (err) {
        console.error('Error verifying webhook:', err);
    }
}

export const GET = handler;
export const POST = handler;
export const PUT = handler;
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • It's giving me the same error. Check the updated question for more details. – Ethan Jul 03 '23 at 18:34
  • I finally managed to update the code. I [could not before today](https://meta.stackoverflow.com/questions/425430/how-can-i-avoid-an-error-occurred-submitting-the-edit-error-message). – VonC Jul 10 '23 at 18:40