You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js集成Clerk Webhook无响应,无法同步用户至MongoDB

Clerk Webhook 无法触发,用户数据无法存入MongoDB的排查方案

问题背景

我在Next.js应用中集成了Clerk认证,想通过Clerk Webhook把用户数据同步到MongoDB。已经部署到Vercel,在Clerk后台配置了${vercel_site}/api/webhook端点,也把Webhook密钥加到了.env.local里,但完全没效果。Clerk提示:

This endpoint has not received any messages yet

控制台没报错,应用运行正常,但就是收不到Clerk的请求。我试过在route.js里加console.log,但没输出,怀疑这个文件根本没被调用,也试过捕获POST请求错误,没发现异常。

相关代码

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) {
  // 从Clerk后台Webhook设置里获取
  const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;

  if (!WEBHOOK_SECRET) {
    throw new Error(
      "请把Clerk后台的WEBHOOK_SECRET添加到.env或.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");

  // 缺少Svix头则返回错误
  if (!svix_id || !svix_timestamp || !svix_signature) {
    return new Response("发生错误 -- 缺少Svix请求头", {
      status: 400,
    });
  }

  // 获取请求体
  const payload = await req.json();
  const body = JSON.stringify(payload);

  // 初始化Svix Webhook实例
  const wh = new Webhook(WEBHOOK_SECRET);

  let evt;

  // 验证请求体和签名
  try {
    evt = wh.verify(body, {
      "svix-id": svix_id,
      "svix-timestamp": svix_timestamp,
      "svix-signature": svix_signature,
    });
  } catch (err) {
    console.error("验证Webhook失败:", err);
    return new Response("发生错误", {
      status: 400,
    });
  }

  // 处理事件
  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("用户已创建或更新", {
        status: 200,
      });
    } catch (err) {
      console.error("创建或更新用户失败:", err);
      return new Response("发生错误", {
        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 } // 不存在则创建新用户
      );
  
      await user.save();
      return user;
    } catch (error) {
      console.error(error);
    }
}

数据库连接代码(mongo.js)

import mongoose from "mongoose";

 let isConnected = false;

 export const connectToDB = async () => {
     mongoose.set("strictQuery", true);

   if (isConnected) {
     console.log("数据库已连接");
   } else {
    try {
      await mongoose.connect(process.env.MONGO_STRING, {
        dbName: "Users",
        useNewUrlParser: true,
         useUnifiedTopology: true,
      });
      isConnected = true;
    } catch (error) {
      console.log(error);
    }
  }
};

可能的问题及排查步骤

1. Vercel环境变量未正确配置

  • 本地.env.local不会自动同步到Vercel,需手动在Vercel项目Settings -> Environment Variables中添加WEBHOOK_SECRET和MONGO_STRING,确保变量名称、值与本地完全一致,无多余空格。
  • 部署后查看VercelFunctions -> Logs,确认是否有WEBHOOK_SECRET未定义的报错。

2. Webhook端点路径错误

  • 检查Clerk后台配置的URL:必须是完整的Vercel域名,比如https://your-vercel-app.vercel.app/api/webhook,不能用占位符。
  • 确认Next.js路由结构:如果使用Pages Router,当前route.js路径错误,需改为pages/api/webhook.js并导出export default function handler(req, res) {...}格式的处理函数。

3. Svix签名验证拦截请求

  • 暂时注释签名验证代码,测试请求是否能到达:
    // 注释验证逻辑,先模拟事件
    // try {
    //   evt = wh.verify(body, { ... });
    // } catch (err) { ... }
    const evt = { type: "user.created", data: payload };
    
    查看Vercel日志是否有输出,确认请求是否进入POST函数。

4. CORS或请求解析问题

  • 针对App Router添加CORS头处理预检请求:
    export async function POST(req) {
      const headers = new Headers();
      headers.set('Access-Control-Allow-Origin', '*');
      headers.set('Access-Control-Allow-Methods', 'POST');
      headers.set('Access-Control-Allow-Headers', 'Content-Type, svix-id, svix-timestamp, svix-signature');
    
      if (req.method === 'OPTIONS') {
        return new Response(null, { headers, status: 200 });
      }
    
      // 原有业务代码...
    }
    
  • 确认req.json()能正确解析Clerk发送的JSON请求体。

5. Clerk Webhook事件未订阅或触发

  • 在Clerk后台Webhook页面,点击Test Webhook手动发送测试事件,查看Vercel日志是否有请求记录。
  • 确认已在Clerk后台Events选项中勾选user.created和user.updated事件。

6. MongoDB连接失败

  • 查看Vercel日志是否有MongoDB连接报错,检查MONGO_STRING是否正确,确认MongoDB实例(如Atlas)的IP白名单允许Vercel访问。

内容的提问来源于stack exchange,提问作者user20706285

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 14:35:02