Skip to content

Conversation

NWylynko
Copy link
Contributor

@NWylynko NWylynko commented Jun 30, 2025

🔎 Previews:

What does this solve?

  • We need a guide on using auth() successfully, staying away from currentUser() and webhooks. Focusing on developer experience and speed

What changed?

  • Wrote guide on creating a custom getUser() function that builds on top of auth() in a performant and successful manor.

Checklist

  • I have clicked on "Files changed" and performed a thorough self-review
  • All existing checks pass

Copy link

vercel bot commented Jun 30, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
clerk-docs ✅ Ready (Inspect) Visit Preview Jul 11, 2025 1:35pm

@jescalan
Copy link
Contributor

jescalan commented Jul 2, 2025

Is there any reason to run this as a guide rather than like, adding it to our SDKs? haha

@NWylynko
Copy link
Contributor Author

NWylynko commented Jul 2, 2025

Is there any reason to run this as a guide rather than like, adding it to our SDKs? haha

I wrote this as like an opinionated way to use clerks features, so feels like a guide to me. But I'm open to moving it.

Copy link
Member

@royanger royanger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dropped a few thoughts in here. This is pretty solid as far as you've written it.


Clerk supports a handful of different ways to manage this disconnect. A lot of people turn to using webhooks to sync the Clerk user data in to their own databases, but this can introduce some out of sync issues.

Here we are going to take a different approach, building on top of Clerk's `auth()` function we are going to create our own function. I'm going to call it `getUser` for simplicity, but feel free to use `authenticateUser`, `authRequest`, `fetchUser`, `verifyAuth` or whatever floats your boat.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of getUser() for two reasons.

One, this is the same as clerkClient().user.getUser() which could confuse customers reading the guide and looking at other parts of the docs or cause confusion with dev success when handling support due to lack of clarity which one a customer might be referring to. Later you also do user.update({}) which ends up matching useUser().

Two, you have .update() so this is no longer just getting but also setting.

This is just thinking about avoiding confusion in the docs and for customers.

So you may ask, well how the hell do I get my users data than? Well it's simply, we just need to modify the session token.

1. Head over to [Sessions](https://dashboard.clerk.com/~/sessions) on the dashboard and scroll down to "Customize session token"
1. Update the claims (if they are not already set to something) to simply include the users email:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth mentioning/linking to info about JWT size limitations. There is a convo in Alexis' PR at #2368 (comment) with lots of info. While just an email is not an issue, for some customers its the start of a slippery slope.


This is cool and all, reducing network requests will keep our application quick and reduces dependency on Clerk infra being available. But as soon as we need to attach a `stripeCustomerId` or a `australianBusinessNumber` your out of luck, right?

Wrong, Clerk has `metadata`, in-fact we have three types, `unsafe`, `private`, and `public`. These live on the user object, are separate and private between users, and let us store any kind of information we want. For each type of metadata, per user we have 4kb of available json, to put that in human terms that's about 2 to 3 pages of text, or about 1,000 words.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the PR comment I linked above I cover this, but metadata can store 8kb. The smaller limits come into play if metadata is being attached to the session. It part of what makes all of this confusing.

Comment on lines +171 to +178
```json
{
"email": "{{user.primary_email_address}}",
// You can call this whatever you'd like,
// I went with 'details' but it's completely up to you
+ "details": "{{user.public_metadata}}"
}
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might be aware, but you could do:

Suggested change
```json
{
"email": "{{user.primary_email_address}}",
// You can call this whatever you'd like,
// I went with 'details' but it's completely up to you
+ "details": "{{user.public_metadata}}"
}
```
```json
{
"email": "{{user.primary_email_address}}",
// You can call this whatever you'd like,
// I went with 'details' but it's completely up to you
"favouriteSnack": "{{user.public_metadata.favoriteSnack}}"
"favouritePizza": "{{user.public_metadata.favoritePizza}}"
}

This is a way of making specific keys available in the session while storing over 4kb of metadata.


Not only do we define that 'details' exists on the users session, but we can now actually in our codebase define our own user data. We no longer need to go over to the dashboard to manage what data we want, it's entirely within our app.

I would start by defining these properties as optional, as any existing users won't have them set. If you're so inclined you could run a migration script to pull the data from say your database and put it in each users metadata, then update the types to make it not optional.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even after backfilling from the DB, there potentially times these are undefined depending on how and when they are set for new users.


I would start by defining these properties as optional, as any existing users won't have them set. If you're so inclined you could run a migration script to pull the data from say your database and put it in each users metadata, then update the types to make it not optional.

This is a standard json object, so stay away from types that can't be json parsed, eg dates, maps, and sets.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...you could add methods to stringify/unstringify arrays in this guide. (probably best to ignore this comment honestly)

"favoritePizza": "Pepperoni",
"favoriteSnack": "Oreos",
"userId": "user_2u823dCrAIzoQfgjnsimCYJvJaI",
"email": "clerk-test@wylynko.me"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"email": "clerk-test@wylynko.me"
"email": "your-email@example.me"

}
```

Our beautiful `getUser()` function gives us both our standard Clerk info (emails, phone numbers, usernames, etc) and has our custom user data.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Our beautiful `getUser()` function gives us both our standard Clerk info (emails, phone numbers, usernames, etc) and has our custom user data.
Our beautiful `getUser()` function gives us both our standard Clerk info (emails, usernames, etc) and has our custom user data.

I know what you mean, but you didn't cover/mention phone numbers so that could be confusing to a customer.

Comment on lines +532 to +535
.returning({
orderId: orders.orderId,
status: orders.status,
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cries in being stuck on MySQL (Planetscale) for support-tools
Please Planetscale, give us access to Postgres so we can consider migrating

}
```

### Migrating data over to metadata
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One huge thing to callout here is that metadata can not be searched. If the customer would need to search by orderId or something else and that is only in metadata they can not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants