Dub 如何配置 Clerk 会话自定义声明并追踪新用户的注册转化事件
【免费下载链接】dubThe modern link attribution platform. Loved by world-class marketing teams like Framer, Perplexity, Superhuman, Twilio, Buffer and more.项目地址: https://gitcode.com/GitHub_Trending/du/dub
如果你的网站使用 Next.js 和 Clerk 做登录,同时通过 Dub 短链接引流,你需要让 Dub 知道"这个新注册的用户来自哪一次链接点击"。Dub 仓库中的 Clerk 集成指南(apps/web/guides/clerk.md)给出的方案是:在用户注册后读取站点上的dub_idCookie(Dub 链接点击后写入的点击标识),调用dub.track.lead上报一次 lead 事件,然后把点击 ID 写回 Clerk 用户的publicMetadata,避免重复上报。同时需要给 Clerk 会话令牌加一个自定义声明,让会话 token 中携带用户的 public metadata。
完成本指南后,一个新用户从 Dub 链接进入站点并注册时,Dub 会把他记录为 customer 并与那次点击事件关联(参见同目录 NextAuth 指南对这一机制的说明),dub_idCookie 随后被删除。
准备工作
指南代码依赖以下前提:
- 一个 Next.js 应用,已接入 Clerk(代码中用到
@clerk/nextjs的useUser与@clerk/nextjs/server的clerkClient); - 安装
@dub/analytics包。按 Dub 的 React 快速上手指南:
npm install @dub/analytics- 在 Clerk 后台创建应用并拿到 Publishable Key 与 Secret Key(指南注释指向 Clerk 后台的建应用页);Dub 的 API Key 则从
d.to/tokens页面获取。
在应用中添加以下环境变量(值需替换为你自己的密钥):
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=your_publishable_key CLERK_SECRET_KEY=your_secret_key DUB_API_KEY=your_api_key给 Clerk 会话令牌添加自定义声明
在 Clerk 的会话令牌配置中,添加如下 JSON 作为自定义声明(custom claim):
{ "metadata": "{{user.public_metadata}}" }其中{{user.public_metadata}}是 Clerk 自定义声明的模板占位符,由 Clerk 在签发令牌时替换为该用户的 public metadata,无需手动替换。
扩展@dub/analytics组件
Dub 的 React 包提供<Analytics />组件负责点击等转化数据的采集。Clerk 指南要求包一层DubAnalytics组件:用 Clerk 的useUserhook 拿到当前用户,当用户已加载、但publicMetadata中还没有dubClickId时(即"尚未持久化到 Dub"的新用户),调用trackLead:
"use client"; import { trackLead } from "@/actions/track-lead"; import { useUser } from "@clerk/nextjs"; import { Analytics, AnalyticsProps } from "@dub/analytics/react"; import { useEffect } from "react"; export function DubAnalytics(props: AnalyticsProps) { const { user } = useUser(); useEffect(() => { if (!user || user.publicMetadata.dubClickId) return; // if the user is loaded but hasn't been persisted to Dub yet, track the lead event trackLead({ id: user.id, name: user.fullName!, email: user.primaryEmailAddress?.emailAddress, avatar: user.imageUrl, }).then(async (res) => { if (res.ok) await user.reload(); else console.error(res.error); }); }, [user]); return <Analytics {...props} />; }然后把DubAnalytics挂到应用根布局中:
import { DubAnalytics } from "@/components/dub-analytics"; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html> <body> <DubAnalytics /> {children} </body> </html> ); }实现trackLeadserver action
在服务端实现被组件调用的trackLead。它的逻辑是:读取dub_idCookie;若存在,则以它作为clickId调用dub.track.lead上报事件名为"Sign Up"的 lead 事件,并把 Cookie 置为过期删除;随后用clerkClient把dubClickId写入该用户的publicMetadata;最后返回{ ok: true },出错时返回{ ok: false, error }:
// This is a server action "use server"; import { dub } from "@/lib/dub"; import { clerkClient } from "@clerk/nextjs/server"; import { cookies } from "next/headers"; export async function trackLead({ id, name, email, avatar, }: { id: string; name?: string | null; email?: string | null; avatar?: string | null; }) { try { const cookieStore = await cookies(); const dubId = cookieStore.get("dub_id")?.value; if (dubId) { // Send lead event to Dub await dub.track.lead({ clickId: dubId, eventName: "Sign Up", customerExternalId: id, customerName: name, customerEmail: email, customerAvatar: avatar, }); // Delete the dub_id cookie cookieStore.set("dub_id", "", { expires: new Date(0), }); } const clerk = await clerkClient(); await clerk.users.updateUser(id, { publicMetadata: { dubClickId: dubId || "n/a", }, }); return { ok: true }; } catch (error) { console.error("Error in trackLead:", error); return { ok: false, error: (error as Error).message }; } }代码中的@/lib/dub是你自己项目里导出 Dub 客户端实例的模块。参考 Dub 的 手动追踪 Lead 指南,客户端用dub包创建,token参数可省略、默认读取DUB_API_KEY环境变量:
import { Dub } from "dub"; export const dub = new Dub({ // optional, defaults to the DUB_API_KEY environment variable token: process.env.DUB_API_KEY, });可选分支:用 API 路由代替 server action
指南同时给出 API 路由方案。客户端侧把trackLead调用换成fetch("/api/track-lead", ...),请求体即用户字段:
fetch("/api/track-lead", { method: "POST", body: JSON.stringify({ id: user.id, name: user.fullName, email: user.primaryEmailAddress?.emailAddress, avatar: user.imageUrl, }), }).then(res => { if (res.ok) await user.reload(); else console.error(res.statusText); });服务端路由的职责与 server action 相同:从req.cookies读取dub_id,存在时上报 lead 事件,调用clerk.users.updateUser写入dubClickId,在响应上把dub_idCookie 置为过期,并返回{ ok: true }。注意指南原文的路由代码直接引用了id、name、email、avatar变量,实现时需要先解析请求 JSON body 得到这些字段(即客户端fetch发送的字段)。
验证与结果判断
指南给出的成功与失败判定来自trackLead的返回值:
- 成功:返回
{ ok: true },客户端useEffect中的.then检测到res.ok后调用user.reload(),重新拉取用户数据——此时该用户的publicMetadata.dubClickId已被写入(有 Cookie 时为实际点击 ID,没有时为"n/a")。 - 失败:返回
{ ok: false, error },客户端把res.error打印到控制台。
另外两个可在浏览器侧核对的状态,均来自指南代码的行为:
- 事件上报成功后
dub_idCookie 被删除(expires: new Date(0)),同一点击不会二次触发上报; - 组件开头有
if (!user || user.publicMetadata.dubClickId) return;,所以已写入dubClickId的用户在后续访问中不会再触发trackLead,lead 事件每次注册只发送一次。
边界说明
- 上报以
dub_idCookie 存在为前提:用户若没有经过 Dub 链接进入站点,事件不会上报,只会把dubClickId写成"n/a"。 - 指南代码基于 Next.js 特性(
next/headers的cookies()、server actions),适用于 Next.js 应用。 - 如果不想走前端组件,Dub 也支持直接用 TypeScript SDK 或 REST API(
https://api.dub.co/track/lead,Authorization: Bearer dub_xxxxxx)手动上报 lead 事件,详见 manual-track-lead.md 与 rest-api.md。
【免费下载链接】dubThe modern link attribution platform. Loved by world-class marketing teams like Framer, Perplexity, Superhuman, Twilio, Buffer and more.项目地址: https://gitcode.com/GitHub_Trending/du/dub
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考