惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

H
Heimdal Security Blog
小众软件
小众软件
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
罗磊的独立博客
Google DeepMind News
Google DeepMind News
大猫的无限游戏
大猫的无限游戏
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Hugging Face - Blog
Hugging Face - Blog
阮一峰的网络日志
阮一峰的网络日志
A
About on SuperTechFans
宝玉的分享
宝玉的分享
博客园 - 聂微东
月光博客
月光博客
Cyberwarzone
Cyberwarzone
Microsoft Security Blog
Microsoft Security Blog
V
Visual Studio Blog
Project Zero
Project Zero
T
Tor Project blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
L
LINUX DO - 最新话题
博客园 - 叶小钗
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Attack and Defense Labs
Attack and Defense Labs
Spread Privacy
Spread Privacy
Forbes - Security
Forbes - Security
Simon Willison's Weblog
Simon Willison's Weblog
N
Netflix TechBlog - Medium
P
Proofpoint News Feed
Engineering at Meta
Engineering at Meta
Hacker News: Ask HN
Hacker News: Ask HN
I
InfoQ
M
MIT News - Artificial intelligence
AI
AI
博客园 - 三生石上(FineUI控件)
W
WeLiveSecurity
C
Check Point Blog
The Hacker News
The Hacker News
C
Cyber Attacks, Cyber Crime and Cyber Security
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
Tenable Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
The Cloudflare Blog
Blog — PlanetScale
Blog — PlanetScale
美团技术团队
D
Darknet – Hacking Tools, Hacker News & Cyber Security
GbyAI
GbyAI
Hacker News - Newest:
Hacker News - Newest: "LLM"
腾讯CDC
K
Kaspersky official blog

Blog — PlanetScale

Keeping a Postgres queue healthy — PlanetScale Patterns for Postgres Traffic Control — PlanetScale Graceful degradation in Postgres — PlanetScale High memory usage in Postgres is good, actually — PlanetScale Stripe Projects partnership: Provision PlanetScale Postgres and MySQL databases from the Stripe CLI — PlanetScale Enhanced tagging in Postgres Query Insights — PlanetScale Behind the scenes: How Database Traffic Control works — PlanetScale Introducing Database Traffic Control — PlanetScale Scaling Postgres connections with PgBouncer — PlanetScale Drizzle joins PlanetScale — PlanetScale Video Conferencing with Postgres — PlanetScale Faster PlanetScale Postgres connections with Cloudflare Hyperdrive — PlanetScale Introducing the PlanetScale MCP server — PlanetScale Database Transactions — PlanetScale Automating our changelog with Cursor commands — PlanetScale Postgres 18 is now available — PlanetScale Using MotherDuck with PlanetScale — PlanetScale $50 PlanetScale Metal is GA for Postgres — PlanetScale AI-Powered Postgres index suggestions — PlanetScale $5 PlanetScale is live — PlanetScale Announcing Vitess 23 — PlanetScale $50 PlanetScale Metal — PlanetScale Report on our investigation of the 2025-10-20 incident in AWS us-east-1 — PlanetScale $5 PlanetScale — PlanetScale Benchmarking Postgres 17 vs 18 — PlanetScale Larger than RAM Vector Indexes for Relational Databases — PlanetScale Partnering with Cloudflare to bring you the fastest globally distributed applications — PlanetScale Processes and Threads — PlanetScale PlanetScale for Postgres is now GA — PlanetScale Postgres High Availability with CDC — PlanetScale Announcing Neki — PlanetScale Caching — PlanetScale The principles of extreme fault tolerance — PlanetScale Announcing PlanetScale for Postgres — PlanetScale Benchmarking Postgres — PlanetScale Announcing Vitess 22 — PlanetScale The Real Failure Rate of EBS — PlanetScale IO devices and latency — PlanetScale Announcing PlanetScale Metal — PlanetScale PlanetScale Metal: There’s no replacement for displacement — PlanetScale Upgrading Query Insights to Metal — PlanetScale Automating cherry-picks between OSS and private forks — PlanetScale Database Sharding — PlanetScale Anatomy of a Throttler, part 3 — PlanetScale Introducing sharding on PlanetScale with workflows — PlanetScale Announcing Vitess 21 — PlanetScale Announcing the PlanetScale vectors public beta — PlanetScale Anatomy of a Throttler, part 2 — PlanetScale Instant deploy requests — PlanetScale Anatomy of a Throttler, part 1 — PlanetScale Increase IOPS and throughput with sharding — PlanetScale Tracking index usage with Insights — PlanetScale Faster backups with sharding — PlanetScale Building data pipelines with Vitess — PlanetScale The State of Online Schema Migrations in MySQL — PlanetScale Optimizing aggregation in the Vitess query planner — PlanetScale Dealing with large tables — PlanetScale Announcing Vitess 20 — PlanetScale Self-managed Vitess vs Managed Vitess with PlanetScale — PlanetScale Achieving data consistency with the consistent lookup Vindex — PlanetScale The MySQL adaptive hash index — PlanetScale Introducing global replica credentials — PlanetScale Profiling memory usage in MySQL — PlanetScale Summer 2023: Fuzzing Vitess at PlanetScale — PlanetScale How PlanetScale makes schema changes — PlanetScale Identifying and profiling problematic MySQL queries — PlanetScale The Problem with Using a UUID Primary Key in MySQL — PlanetScale Announcing Vitess 19 — PlanetScale PlanetScale forever — PlanetScale Introducing schema recommendations — PlanetScale Amazon Aurora Pricing: The many surprising costs of running an Aurora database — PlanetScale Three common MySQL database design mistakes — PlanetScale OAuth applications are now available to everyone — PlanetScale Deprecating the Scaler plan — PlanetScale PlanetScale branching vs. Amazon Aurora blue/green deployments — PlanetScale Databases at scale — PlanetScale Considerations for building a database disaster recovery plan — PlanetScale Working with Geospatial Features in MySQL — PlanetScale PlanetScale vs Amazon Aurora replication — PlanetScale Introducing the Vantage and PlanetScale integration — PlanetScale MySQL isolation levels and how they work — PlanetScale Introducing the schemadiff command line tool — PlanetScale $ pscale ping — PlanetScale Announcing foreign key constraints support — PlanetScale The challenges of supporting foreign key constraints — PlanetScale What is HTAP? — PlanetScale Introducing Insights Anomalies — PlanetScale Webhook security: a hands-on guide — PlanetScale MySQL replication: Best practices and considerations — PlanetScale A guide to HTML email with Ruby on Rails and Tailwind CSS — PlanetScale Sharding for cost-effective database management — PlanetScale PlanetScale ranks 188th in Deloitte’s top 500 fastest-growing companies — PlanetScale Announcing the Fivetran integration — PlanetScale Introducing webhooks — PlanetScale What is MySQL replication and when should you use it? — PlanetScale Introducing database reports — PlanetScale Distributed caching systems and MySQL — PlanetScale What is MySQL partitioning? — PlanetScale MySQL High Availability: Connection handling and concurrency — PlanetScale Personalizing your onboarding with Markdoc — PlanetScale
Sync user data between Clerk and a PlanetScale MySQL database — PlanetScale
Brian Morrison II · 2023-10-20 · via Blog — PlanetScale

Brian Morrison II |

A common strategy of modern development, especially in serverless applications, is to offload user management to third-party authentication services, commonly referred to as Identity Providers (IdP). While this shifts the focus and responsibility of managing your users’ data to another organization, implementing this strategy brings several tradeoffs:

  1. You don’t have direct access to user data within your own systems.
  2. You can’t perform secondary actions when a user changes their data in some way.
  3. Any API calls where the user’s info is used would require you to call the IdP, making the responses take longer.

In this article, you’ll learn how to address each of these issues using webhooks, a pattern where the IdP can send an HTTP call to your API to inform you of any changes made on their end. To do this, you’ll be using a combination of Clerk, Netlify, and PlanetScale.

For the demo, we’ll be using Orbytal.ink, an open-source “link in bio” web application that uses the services listed above. By the end of this guide, Clerk will be configured to send an HTTP request to a Netlify Function designed specifically for accepting these requests whenever a user is created, updated, or deleted.

To follow along, make sure you have the following:

Set up the project

Create a PlanetScale database

Start by creating a PlanetScale database using the PlanetScale CLI.

pscale database create orbytalink

Next, create a password for that database. The name is arbitrary but the following command uses “defaultpass”:

pscale password create orbytalink main defaultpass

Note

Take note of the username and password — you’ll be using those values when setting up Netlify.

Now, open a shell session to the new database you just created:

pscale shell orbytalink main

To configure the tables for the database, run the following command in the pscale shell session:

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)
);

Set up a Clerk application

Head over to Clerk and create a new application. Make sure to only check "Username" in the "How will your users sign in?" section, then click "Create application".

Create a new application in Clerk.

Next, you’ll have to configure a few more options before the project is properly configured. Select "Users & Authentication" > "Email, Phone, Username" on the left side. Then click the "gear" next to "Email address".

Update the email address setting.

In the modal, enable the "Require" option and make sure "Email verification code" is enabled. Everything else should be disabled. Click "Continue" to accept the settings.

Set email address to required.

Scroll down a bit and enable "Name", then click the "gear icon".

Update the name settings.

In the modal, toggle "Require" to on and click "Continue".

Set name to required.

Scroll to the bottom of the page and click "Apply changes".

Now you’ll need to grab the API keys for the project so the application will work when deployed. Select "API Keys" on the left and take note of both the "Publishable key" and the "Secret key". You’ll need these values as well when setting up Netlify.

Note

Anyone who has these values can access your Clerk project, so make sure to keep these values secret!

Get the Clerk API keys.

Fork the project and deploy to Netlify

Next, log into GitHub and fork the orbytal.ink project to your own account. Make sure "Copy the main branch only" is NOT enabled as you’ll want all branches forked into your account.

Log into your Netlify account, and click "Add new site" > "Import an existing project".

Create a new Netlify site.

Select "Deploy with GitHub".

Deploy with GitHub.

Next, select "Orbytal.ink" from the list of projects in your account.

Select Orbytal.ink in Netlify.

Make sure to select the clerk-blog-post branch under "Branch to deploy". Then, scroll to the bottom and click "Deploy Orbytal.ink".

Deploy the clerk-blog-post branch.

Once the initial deployment is done, you’ll need to configure a few environment variables. Select "Site configuration" from the sidebar, and then "Environment variables". Add the following variables:

  • DATABASE_URL: The connection string for your PlanetScale database formatted as mysql://<DB_USERNAME>:<DB_PASSWORD>@aws.connect.psdb.cloud/orbytalink and replace DB_USERNAME and DB_PASSWORD with the values created earlier in the guide.
  • CLERK_API_KEY: The private key acquired when configuring the Clerk project.
  • VITE_CLERK_PUBLISHABLE_KEY: The public key acquired when configuring the Clerk project.

Add environment variables.

Finally, re-deploy the project with the new settings by going back to "Deploys", and then "Trigger deploy" > "Deploy site".

Redeploy the Netlify site.

To test that your deployment works, navigate to the URL provided by Netlify and you should see the following if everything was built and deployed correctly.

Demo site landing page.

Add webhooks

Now that your project is up and running, let's add the webhook so we can pass specific user information to the PlanetScale database as users are created or updated.

Configure Clerk webhooks

In the overview of the Clerk project, select "Webhooks" from the sidebar, and then "Add endpoint".

Add webhook endpoint.

Enter the "Endpoint URL" formatted as <NETLIFY_URL>/.netlify/functions/clerk_webhook, select the topmost user element under "Message Filtering", and scroll to the bottom and click "Create".

Set endpoint URL and events.

Create and deploy the Netlify Function

Next, you’ll need to create a Netlify Function that will act as the endpoint to receive messages from our Clerk project. Luckily, all of this work can be done directly within GitHub. With the project open in GitHub, start by switching to the clerk-blog-post branch.

Switch to clerk-blog-post branch in GitHub.

Click "Add file" > "Create new file".

Create a new file in GitHub.

Name the file functions/clerk_webhook.ts and paste the following into that file. Note the comments in the code that describe what the important parts of the function do.

// 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 }

Click on "Commit changes" to open a modal. Feel free to add an extended description if you like, and click "Commit changes" in the modal to create the file in the branch.

Commit new file in GitHub.

Once the file is saved, Netlify should automatically deploy the latest version of the web application.

Test the function

Now that the new code is deployed, we can test the three main operations that were configured in Clerk. Open the web app using the Netlify address and click "Create your profile".

Create your profile on the demo site.

Create an account using your own email address. When prompted for a verification code, grab it from your email and enter it.

Create a user in Clerk.

Note

If you get a message stating "The authentication settings are invalid", be sure to double check your configuration in Clerk.

Once the account is created, you should be redirected back to your version of Orbytalink asking for some more details. Add a tagline and click "New block". Select "Twitter" and enter your Twitter username. Finally, click "Save".

Add user settings.

You should be redirected to your profile that not only shows you the tagline and Twitter block but also your name and username you entered into the Clerk sign-up form. That’s because when you created your account, Clerk sent a message to the Netlify Function you created earlier (clerk_webhook.ts) which saved this information into the PlanetScale database.

User profile in demo site.

If you explore the Netlify function that is used by the home page to retrieve user data, you’ll notice that there are no API calls to Clerk. It grabs information directly from the PlanetScale database and returns it to the React front end.

// 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 }

The following sequence diagram explains exactly how this overall system works:

  1. The user creates an account in Clerk.
  2. Clerk sends a message to the Netlify function once the user is created.
  3. Netlify writes that users’ info to the PlanetScale database.

Webhooks sequence diagram.

Using this flow, we can utilize Clerk to handle authentication and user management, and still have the users’ information available to us directly in our PlanetScale database!

Conclusion

Webhooks are extremely useful when using third-party systems where you need to be notified if a specific event happens in a place you don’t have full access to. In this guide, I showed you how you can use webhooks to receive user information from an IdP, but that’s only one example where these can be used in a production system.

Have you used webhooks in your own projects? Let us know on Twitter by tagging @planetscale!