After successfully implementing clerk authentication in my nextjs app, I encountered an issue with saving users in MongoDB through clerk webhook. Even though I have hosted my application on Vercel, added the ${vercel_site}/api/webhook
endpoint in clerk, and included the clerk webhook key in .env.local
, nothing seems to be working. On the clerk dashboard, I keep seeing the message:
This endpoint has not received any messages yet.
Although there are no errors in the console and the application appears to be functioning properly, I am not receiving any response from clerk. What could possibly be the issue?
api/webhook/route.js
import { Webhook } from "svix";
import { headers } from "next/headers";
import { createOrUpdateUser } from "../../lib/controllers/user.controller.js";
export async function POST(req) {
// Relevant webhook secret from Clerk Dashboard goes here
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;
if (!WEBHOOK_SECRET) {
throw new Error(
"Please add WEBHOOK_SECRET from Clerk Dashboard to .env or .env.local"
);
}
// Get the headers
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 there are no headers, error out
if (!svix_id || !svix_timestamp || !svix_signature) {
return new Response("Error occured -- no svix headers", {
status: 400,
});
}
// Get the body
const payload = await req.json();
const body = JSON.stringify(payload);
// Create a new Svix instance with your secret.
const wh = new Webhook(WEBHOOK_SECRET);
let evt;
// Verify the payload with the headers
try {
evt = wh.verify(body, {
"svix-id": svix_id,
"svix-timestamp": svix_timestamp,
"svix-signature": svix_signature,
});
} catch (err) {
console.error("Error verifying webhook:", err);
return new Response("Error occurred", {
status: 400,
});
}
// Handle the event
const eventType = evt?.type;
if (eventType === "user.created" || eventType === "user.updated") {
const { id, first_name, last_name, profile_image_url, email_addresses } =
evt?.data;
try {
await createOrUpdateUser(
id,
first_name,
last_name,
profile_image_url,
email_addresses,
);
return new Response("User is created or updated", {
status: 200,
});
} catch (err) {
console.error("Error creating or updating user:", err);
return new Response("Error occurred", {
status: 500,
});
}
}
}
user.controller.js
import User from '../models/Users.model.js';
import { connectToDB } from '../mongo.js';
export const createOrUpdateUser = async(id, first_name, last_name, profile_image_url, email_addresses) => {
try{
await connectToDB();
const user = await User.findOneAndUpdate(
{ clerkId: id },
{
$set: {
firstName: first_name,
lastName: last_name,
profilePhoto: profile_image_url,
email: email_addresses[0].email_address,
},
},
{ upsert: true, new: true } // if user doesn't exist, create a new one
);
await user.save();
return user;
} catch (error) {
console.error(error);
}
}
Connection to database
import mongoose from "mongoose";
let isConnected = false;
export const connectToDB = async () => {
mongoose.set("strictQuery", true);
if (isConnected) {
console.log("Database is connected");
} else {
try {
await mongoose.connect(process.env.MONGO_STRING, {
dbName: "Users",
useNewUrlParser: true,
useUnifiedTopology: true,
});
isConnected = true;
} catch (error) {
console.log(error);
}
}
};
Within route.js, I attempted to
console.log(svix_id, svix_timestamp, svix_signature)
but received no response. This raises the question of whether this file is being called at all. Additionally, I set up error handling in case the POST request fails, but no errors have been detected thus far.
catch (err) {
console.error("Error creating or updating user:", err);
return new Response("Error occurred", {
status: 500,
});
}