Need clerk webhook example

I have been trying to configure a webhook to be called as soon an organization has been created and the webhook should call a prisma query to do a DB entry but I couldnt find any example that would help me configure this. I am using pages router.
W
whatplan326d ago
I know for sure there are examples in the clerk server (maybe even in the clerk docs) maybe even in here too just search for them and see what you can find here is what I have in a code base I wrote 3 months ago following something from the clerk server
import type { IncomingHttpHeaders } from "http";
import type { NextApiRequest, NextApiResponse } from "next";
import type { WebhookRequiredHeaders } from "svix";
import type { User } from "@clerk/nextjs/dist/api";
import { Webhook } from "svix";
import { prisma } from "~/server/clients/db";
import { env } from "~/env.mjs";

type UnwantedKeys =
| "emailAddresses"
| "firstName"
| "lastName"
| "primaryEmailAddressId"
| "primaryPhoneNumberId"
| "phoneNumbers";

interface UserInterface extends Omit<User, UnwantedKeys> {
email_addresses: {
email_address: string;
id: string;
}[];
primary_email_address_id: string;
first_name: string;
last_name: string;
primary_phone_number_id: string;
phone_numbers: {
phone_number: string;
id: string;
}[];
}

const webhookSecret: string = env.CLERK_WEBHOOK_SECRET || "";

export default async function handler(
req: NextApiRequestWithSvixRequiredHeaders,
res: NextApiResponse
) {
const payload = JSON.stringify(req.body);
const headers = req.headers;
const wh = new Webhook(webhookSecret);
let evt: Event | null = null;
try {
evt = wh.verify(payload, headers) as Event;
} catch (_) {
return res.status(400).json({ message: "Invalid signature" });
}
const { id } = evt.data;
// Handle the webhook
const eventType: EventType = evt.type;
if (eventType === "user.created" || eventType === "user.updated") {
const { email_addresses, primary_email_address_id, username } = evt.data;
const emailObject = email_addresses?.find((email) => {
return email.id === primary_email_address_id;
});
if (!emailObject || !username) {
return res.status(400).json({ message: "Missing email or username" });
}
await prisma.account.upsert({
where: { clerkId: id },
update: {
username: username,
email: emailObject.email_address,
},
create: {
clerkId: id,
username: username,
email: emailObject.email_address,
},
});
}
console.log(`User ${id} was ${eventType}`);
res.status(201).json({ message: "OK" });
}

type NextApiRequestWithSvixRequiredHeaders = NextApiRequest & {
headers: IncomingHttpHeaders & WebhookRequiredHeaders;
};

type Event = {
data: UserInterface;
object: "event";
type: EventType;
};

type EventType = "user.created" | "user.updated" | "*";
import type { IncomingHttpHeaders } from "http";
import type { NextApiRequest, NextApiResponse } from "next";
import type { WebhookRequiredHeaders } from "svix";
import type { User } from "@clerk/nextjs/dist/api";
import { Webhook } from "svix";
import { prisma } from "~/server/clients/db";
import { env } from "~/env.mjs";

type UnwantedKeys =
| "emailAddresses"
| "firstName"
| "lastName"
| "primaryEmailAddressId"
| "primaryPhoneNumberId"
| "phoneNumbers";

interface UserInterface extends Omit<User, UnwantedKeys> {
email_addresses: {
email_address: string;
id: string;
}[];
primary_email_address_id: string;
first_name: string;
last_name: string;
primary_phone_number_id: string;
phone_numbers: {
phone_number: string;
id: string;
}[];
}

const webhookSecret: string = env.CLERK_WEBHOOK_SECRET || "";

export default async function handler(
req: NextApiRequestWithSvixRequiredHeaders,
res: NextApiResponse
) {
const payload = JSON.stringify(req.body);
const headers = req.headers;
const wh = new Webhook(webhookSecret);
let evt: Event | null = null;
try {
evt = wh.verify(payload, headers) as Event;
} catch (_) {
return res.status(400).json({ message: "Invalid signature" });
}
const { id } = evt.data;
// Handle the webhook
const eventType: EventType = evt.type;
if (eventType === "user.created" || eventType === "user.updated") {
const { email_addresses, primary_email_address_id, username } = evt.data;
const emailObject = email_addresses?.find((email) => {
return email.id === primary_email_address_id;
});
if (!emailObject || !username) {
return res.status(400).json({ message: "Missing email or username" });
}
await prisma.account.upsert({
where: { clerkId: id },
update: {
username: username,
email: emailObject.email_address,
},
create: {
clerkId: id,
username: username,
email: emailObject.email_address,
},
});
}
console.log(`User ${id} was ${eventType}`);
res.status(201).json({ message: "OK" });
}

type NextApiRequestWithSvixRequiredHeaders = NextApiRequest & {
headers: IncomingHttpHeaders & WebhookRequiredHeaders;
};

type Event = {
data: UserInterface;
object: "event";
type: EventType;
};

type EventType = "user.created" | "user.updated" | "*";
S
Sugan_Selvam325d ago
@whatplan (Rustular Devrel) this works locally ?? cos i used the code but somehow it not running
JP
James Perkins325d ago
I don't have an example in the current docs that is super in depth but this new doc here: https://beta-docs.clerk.com/users/guides/sync-data-to-your-backend Tells you how to use everything, note that testing locally you need to expose your endpoint outside of your local application as a webhook is a reverse API
Sync data to your backend with webhooks | Clerk
Authentication and User management for the modern web
S
Sugan_Selvam324d ago
@James Perkins , Thanks. I was missing the ngrok. This docs helped me to fix it.
AH
Ahmed Heikal104d ago
Hey everyone, when I sync clerk data to my back end using webhook and do all steps i receive 401 error on vercel Log and this is my link of problem on stackoverflow : https://stackoverflow.com/questions/77768548/401-unauthorised-when-processing-a-clerk-webhook-in-next-js
Stack Overflow
401 Unauthorised when processing a Clerk Webhook in Next.js
when i sync clerk data to my back end using webhook and do all steps i recive 401 error on vercel Log this is app/api/webhook/route.ts /* eslint-disable camelcase */ import { Webhook } from "s...
Want results from more Discord servers?
Add your server
More Posts
GitHub Action Failing Type-Checks but Working LocallyI'm setting up an automatic github action that will run type checks on my nextjs app when pushed / plooking for some tutorialscoming from a rails background, im new to node & t3 in general, is there a tutorial i can follow to is it worth it to have meaningful errors returned from server side form validation?like is it worth it at that point to return an error for every field that is invalid from the backenGood setup for CRA + Serverless in a monorepo?Hey folks, what’s a good setup for a serverless function and a CRA in a monorepo? If I’m able to stHow to redirect with new headers back to the same page?User goes to page `/foo`, we check if this page is protected, if it is, we rewrite the page to sometThe Edge - what's the pointso as far as i know the edge move a server close as possible to your user right? but then what's thcloudflare R2 in Versel?Has anyone here used R2 in their NextJS app hosted on Versel? Trying to save money on bandwidth WhReact.FC equivalent for RSCIs there a "React.FC" equivalent for React Server Components?How do I use the return type of a function with overloads that will be returned in the same functionI'm creating a library, but I have a problem when I use a function with overloads to return itself iMedia not rendering on first try, only after a refreshHey, first of all, this is my first complex Next.js project, so it is very probable that I am doing any good resource for browser extension development?just titleRoute Guarding with nextjsHow do you implement route guarding with nextjs? Not too sure what would be the recommended way to dthe URL must start with the protocol `mongo`I have been geting this error recently, and I have no idea why. my mongodb DATABASE_URL is without "Read and Write in Next Js Is not working in production (vercel)this code is working in local environment but not working in production can anyone tell me how to re