现代开发中一种常见的策略,尤其是在无服务器应用程序中,是将用户管理外包给第三方身份验证服务,通常称为身份提供商(IdP)。虽然这将管理用户数据的焦点和责任转移到了另一个组织,但实施这一策略会带来几个权衡:
你无法直接访问自己系统内的用户数据。
当用户以某种方式更改其数据时,你无法执行次要操作。
任何使用用户信息的 API(应用程序编程接口) 调用都需要你调用 IdP,导致响应时间更长。
在本文中,你将学习如何使用 webhook 来解决这些问题,webhook 是一种模式,IdP 可以向你的 API 发送 HTTP 调用,以通知你他们端所做的任何更改。为此,你将结合使用 Clerk、Netlify 和 PlanetScale。
对于演示,我们将使用 Orbytal.ink,这是一个使用上述服务的开源“链接在个人简介中”Web 应用程序。在本指南结束时,Clerk 将被配置为每当用户被创建、更新或删除时,向专门设计用于接受这些请求的 Netlify Function 发送 HTTP 请求。
要继续学习,请确保你具备以下条件:
一个 PlanetScale 账户,以及已安装并配置好的 PlanetScale CLI
一个 GitHub 账户
一个 Netlify 账户
一个 Clerk 账户
首先使用 PlanetScale CLI 创建一个 PlanetScale 数据库。
pscale database create orbytalink
接下来,为该数据库创建一个密码。名称是任意的,但以下命令使用了“defaultpass”:
pscale password create orbytalink main defaultpass
记下用户名和密码——在设置 Netlify 时你将用到这些值。
现在,打开一个 shell 会话连接到刚刚创建的新数据库:
pscale shell orbytalink main
要为数据库配置表,请在 pscale shell 会话中运行以下命令:
CREATE TABLE ` users ` ( `id` bigint unsigned PRIMARY KEY AUTO_INCREMENT, `username` varchar ( 120 ), `tagline` varchar ( 250 ), `display_name` varchar ( 250 ), `img_url` varchar ( 500 ) ); CREATE TABLE ` blocks ` ( `id` bigint unsigned PRIMARY KEY AUTO_INCREMENT, `url` varchar ( 200 ), `type` int , `user_id` int , `label` varchar ( 200 ) );
有关 PlanetScale 的高层次概述,请查看我们的 Introduction to PlanetScale Tech talk。
前往 Clerk 并创建一个新应用程序。确保在“How will your users sign in?”部分仅勾选“Username”,然后点击“Create application”。

接下来,在项目正确配置之前,你还需要配置几个选项。在左侧选择“Users & Authentication” > “Email, Phone, Username”。然后点击“Email address”旁边的“齿轮”图标。

在模态框中,启用“Require”选项,并确保“Email verification code”已启用。其他所有选项都应禁用。点击“Continue”以接受设置。

向下滚动一点,启用“Name”,然后点击“齿轮图标”。

在模态框中,将“Require”切换为开启,然后点击“Continue”。

滚动到页面底部,点击“Apply changes”。
现在你需要获取项目的 API 密钥,以便应用程序在部署时能够正常工作。在左侧选择“API Keys”,并记下“Publishable key”和“Secret key”。在设置 Netlify 时,你也将需要这些值。
任何拥有这些值的人都可以访问你的 Clerk 项目,因此请务必对这些值保密!

接下来,登录 GitHub 并将 orbytal.ink 项目复刻到你自己的账户。确保未启用“仅复制主分支”,因为你需要将所有分支复刻到你的账户中。
登录你的 Netlify 账户,然后点击“Add new site” > “Import an existing project”。

选择“Deploy with GitHub”。

接下来,从你账户的项目列表中选择“Orbytal.ink”。

确保在“Branch to deploy”下选择 clerk-blog-post 分支。然后,滚动到底部并点击“Deploy Orbytal.ink”。

初始部署完成后,你需要配置一些环境变量。从侧边栏选择“Site configuration”,然后选择“Environment variables”。添加以下变量:
DATABASE_URL:你的 PlanetScale 数据库的连接字符串,格式为 mysql://
CLERK_API_KEY:配置 Clerk 项目时获取的私钥。
VITE_CLERK_PUBLISHABLE_KEY:配置 Clerk 项目时获取的公钥。

最后,返回“Deploys”,然后点击“Trigger deploy” > “Deploy site”,使用新设置重新部署项目。

要测试你的部署是否正常工作,请导航到 Netlify 提供的 URL,如果一切构建和部署正确,你应该会看到以下内容。

现在你的项目已经启动并运行,让我们添加 webhook,以便在用户创建或更新时将特定用户信息传递给 PlanetScale 数据库。
在 Clerk 项目的概览中,从侧边栏选择 "Webhooks",然后选择 "Add endpoint"。

输入格式为

接下来,你需要创建一个 Netlify Function,它将作为接收来自 Clerk 项目消息的端点。幸运的是,所有这些工作都可以直接在 GitHub 中完成。在 GitHub 中打开项目后,首先切换到 clerk-blog-post 分支。

点击 "Add file" > "Create new file"。

将文件命名为 functions/clerk_webhook.ts,并将以下内容粘贴到该文件中。请注意代码中描述函数重要部分作用的注释。
// functions/clerk_webhook.ts
import
{
HandlerEvent
,
HandlerContext
}
from
'@netlify/functions'
import
{
getDb
}
from
'./utils/lib'
import
{
blocks
,
users
}
from
'./utils/db/schema'
import
{
eq
}
from
'drizzle-orm'
// This type describes the structure of the incoming webhook
type
ClerkWebhook
=
{
data
:
{
first_name
:
string
last_name
:
string
image_url
:
string
username
:
string
}
type
:
string
}
const
handler
=
async
(
event
:
HandlerEvent
,
context
:
HandlerContext
)
=>
{
if
(
event
.
body
)
{
// 👉 Parse the incomign event body into a ClerkWebhook object
const
webhook
=
JSON
.
parse
(
event
.
body
)
as
ClerkWebhook
try
{
const
db
=
getDb
()
// 👉 `webhook.type` is a string value that describes what kind of event we need to handle
// 👉 If the type is "user.updated" the important values in the database will be updated in the users table
if
(
webhook
.
type
===
'user.updated'
)
{
await
db
.
update
(
users
)
.
set
({
display_name
:
`
${
webhook
.
data
.
first_name
}
${
webhook
.
data
.
last_name
}
`
,
img_url
:
webhook
.
data
.
image_url
})
.
where
(
eq
(
users
.
username
,
webhook
.
data
.
username
))
}
// 👉 If the type is "user.created" create a record in the users table
if
(
webhook
.
type
===
'user.created'
)
{
await
db
.
insert
(
users
).
values
({
display_name
:
`
${
webhook
.
data
.
first_name
}
${
webhook
.
data
.
last_name
}
`
,
img_url
:
webhook
.
data
.
image_url
,
username
:
webhook
.
data
.
username
})
}
// 👉 If the type is "user.deleted", delete the user record and associated blocks
if
(
webhook
.
type
===
'user.deleted'
)
{
const
dbuser
=
await
db
.
query
.
users
.
findFirst
({
where
:
eq
(
users
.
username
,
webhook
.
data
.
username
)
})
console
.
log
(
'dbuser'
,
dbuser
)
if
(
dbuser
)
{
await
Promise
.
all
([
db
.
delete
(
users
).
where
(
eq
(
users
.
id
,
dbuser
.
id
)),
db
.
delete
(
blocks
).
where
(
eq
(
blocks
.
user_id
,
dbuser
.
id
))
])
}
}
return
{
statusCode
:
200
}
}
catch
(
err
)
{
console
.
error
(
err
)
return
{
statusCode
:
500
}
}
}
}
export
{
handler
}点击 "Commit changes" 打开一个模态框。如果需要,可以随意添加扩展描述,然后点击模态框中的 "Commit changes" 在分支中创建该文件。

文件保存后,Netlify 应会自动部署 Web 应用程序的最新版本。
现在新代码已部署,我们可以测试在 Clerk 中配置的三个主要操作。使用 Netlify 地址打开 Web 应用,然后点击 "Create your profile"。

使用你自己的电子邮件地址创建一个账户。当提示输入验证码时,从你的电子邮件中获取并输入它。

如果你收到一条消息说“认证设置无效”,请务必仔细检查你在 Clerk 中的配置。
账户创建后,你应该会被重定向回你的 Orbytalink 版本,要求提供更多详细信息。添加一个标语并点击“New block”。选择“Twitter”并输入你的 Twitter 用户名。最后,点击“Save”。

你应该会被重定向到你的个人资料页面,该页面不仅显示标语和 Twitter 区块,还显示你在 Clerk 注册表单中输入的名字和用户名。这是因为当你创建账户时,Clerk 向你之前创建的 Netlify Function(clerk_webhook.ts)发送了一条消息,该函数将此信息保存到了 PlanetScale 数据库中。

如果你查看主页用于检索用户数据的 Netlify function,你会注意到没有对 Clerk 的 API 调用。它直接从 PlanetScale 数据库获取信息并将其返回给 React 前端。
// functions/profiles.ts
import
{
HandlerEvent
,
HandlerContext
}
from
'@netlify/functions'
import
{
users
}
from
'./utils/db/schema'
import
{
eq
}
from
'drizzle-orm'
import
{
createResponse
}
from
'./utils/netlify_helpers'
import
{
getDb
}
from
'./utils/lib'
const
handler
=
async
(
event
:
HandlerEvent
,
context
:
HandlerContext
)
=>
{
const
{
username
}
=
event
.
queryStringParameters
as
any
const
db
=
getDb
()
if
(
username
)
{
const
user
=
await
db
.
query
.
users
.
findFirst
({
where
:
eq
(
users
.
username
,
username
),
with
:
{
blocks
:
true
}
})
return
createResponse
(
200
,
user
)
}
else
{
const
user_rows
=
await
db
.
select
().
from
(
users
).
limit
(
30
)
return
createResponse
(
200
,
user_rows
)
}
}
export
{
handler
}以下序列图准确解释了整个系统的工作原理:
用户在 Clerk 中创建一个账户。
用户创建后,Clerk 向 Netlify function 发送一条消息。
Netlify 将该用户的信息写入 PlanetScale 数据库。

使用这个流程,我们可以利用 Clerk 来处理认证和用户管理,同时仍然可以直接在我们的 PlanetScale 数据库中获取用户信息!
当你使用第三方系统时,如果你需要在你没有完全访问权限的地方发生特定事件时收到通知,Webhooks 非常有用。在本指南中,我向你展示了如何使用 webhooks 从 IdP 接收用户信息,但这只是它们在生产系统中使用的一个例子。
转载声明:本文为转载整理,原文版权归原作者及原站点所有。
原文来源:PlanetScale Blog
原创地址:https://planetscale.com/blog/sync-user-data-between-clerk-and-a-planetscale-database
原文作者:PlanetScale Blog;如有侵权,请联系管理员删除。