Clerk + Convex: is there a need for a user table in convex?
I'm building a project with clerk and convex. I was wondering if there's a need to implement a user schema in the convex db if clerk already stores this info (which we're getting after authenticating with clerk on the frontend).
Seems a bit unnecessary to have a custom table for this if clerk already stores it.
whats the best practice here?
22 Replies
Just speaking as a user, it's really a matter of preference. I'm not aware of any reason you actually need a users table if you're using the Clerk integration. I personally prefer to not have all of my user data with one provider, so I keep a users table effectively as a proxy, but I could easily drop that and just use Clerk and it wouldn't affect anything.
@erquhart is right, there are pros and cons to storing user information in your DB, but it's definitely not required if you're happy to rely on Clerk/Auth0 or another OAuth provider.
@Michal Srb can you elaborate on those pros and cons?
I'm sure Michael has a better answer, but in my experience w/ Clerk (both on Convex and other dbs), it boils down to having one source of truth or two. If you use Clerk, your users table in Convex can potentially get out of sync, for example. A single source of truth is usually what you want, and it's the scenario providers like Clerk and Auth0 are primarily designed for, so you're doing some extra work and technically adding a slight potential for a bug or two if the syncing ever fails, as with any multiple source of truth situation.
If you're looking for a proper best practice, I would look to the providers you're using. Clerk will probably say best practice is to just source your user info with them. I'm likely going to end up doing this myself, just haven't gotten around to reversing my initial setup.
The reason it might be beneficial to do the syncing into Convex is if you want to build custom queries that merges data between user and non-user tables in the Convex
query
function.In particular, if you're trying to figure out inside of a query or mutation who the user is, what team they're on, what documents they should have access to, etc, you'll want some notion of the user accessible in the database, since query / mutation functions don't have access to http calls.
However, if this is all done just with a clerk ID passed in the auth
sub
field, you might not need a users table.
Another concrete example is if you're trying to show the names of users in your app via a query, you won't have access to any user names except the logged in user (via ctx.auth).
My personal preference is to have all my data in one place and have the source of truth be the table I control, and not use Clerk's user table / user mgmt functionality at all. When I'm first getting started, though, I'm ok with a split brain approach so I don't have to build it all from scratch. Classic build vs. buy heuristic: buy now, build later if my app succeedsin general I would suggest a users table in convex. you eventually want to have user information inside a transactional context with all the rest of your app data
convex lets you do that. if you're spanning your user information into a non-transactional third party (api call to clerk), life can get a lot more brittle down the road
Really good info here. A stack article on this specifically (user data splitting between convex and auth providers) would be beneficial, hadn't considered some of these points.
I can second that an article or full demo would be great. I'm building an app and it took me nearly a weak experimenting with different approaches with clerk and convex for better authentication and authorizations flows. For my case at least it was too painful to have much of auth logic unaccessible by the app, hence I decided to use clerk just for auth, and handle organisations, teams, roles all within convex.
GitHub
GitHub - thomasballinger/convex-clerk-users-table
Contribute to thomasballinger/convex-clerk-users-table development by creating an account on GitHub.
That project from out templates is our current recommended best practice. I.e., use a clerk web hook to keep user changes propagating into your convex users table.
Also agree that all authz should be in convex.
Found that project here: https://www.convex.dev/templates?search=Clerk
Templates
The backend application platform with everything you need to build your product.
We tend to keep those templates in good shape.
thanks for all the info, super helpful!
just wanted to clarify, when you say that all authz should be done in convex, do you mean authorization to see certain data within convex?
Yep. Keep your rule expressions in convex. Here’s one approach: https://stack.convex.dev/row-level-security
Row Level Security: Wrappers as "Middleware"
Implementing row-level security on Convex as a library. Wrap access to the database with access checks written in plain old JS / TS.
In general people use wrappers around their query handlers that encapsulate authz logic.
Where that authz is also using account information from convex tables.
Yup I used that and it was a great starting point, unfortunately the example doesn't consider authorizations. For my case I started by doing the same with the for organisations and memberships as the user table (sync with clerk), this turned out to be a really buggy approach with too many edge cases. Like for example when I hit use case where I need to support document sharing with team members and external collaborators. I had to navigate how to handle adding collaborators that don't exist on the user table, find out that you can't trigger actions inside a query, and the limitation of the clerk api when it doesn't have the request context.
A template with (even opinionated one ) that just work would be a huge relief.
Gotcha. Yeah, you’d want some sort of membership table that was the Union of your logged on users and third party users. We’ll have to think about better examples here, good feedback.
This is a bit of a 🌶️ take, but while I love clerk, the amount of functions they want to own about org structure ends up being problematic in essentially any system.
interesting, didnt really think about that.
what kind of problems show up? is there a lot of data syncing that needs to be done down the road?
what kind of problems show up? is there a lot of data syncing that needs to be done down the road?
The kind of data they value-add allow you to manage in their system is very “root” in eventual dependency trees that get threaded throughout your system. Like authorization rules.
It becomes fragile over time to have those mutate outside of the rules of your consistent, transactional OLTP context.
The best place to attach rich third party data is at the edges of your dependency trees. Creating inversions like that can be tricky to reason about. The database should be “the bottom” as much as possible.
I came across this thread after having the exact same question, should I have a users table and not just rely on Clerk to handle that. It seems as though the answer to this is yes but there are (or were) some issues with this in general from the messages above.
So, in March 2024, is the recommended approach still pretty much the same? Is the implementation the same. Is it robust? Would love to know if there are any updates to using Clerk (or any other auth provider) with Convex in a stable/robust way that you can effectively set up and just forget about.
Still recommended and, imo, quite robust. You’ll want to use webhooks to sync changes to Clerk user data, beyond that you can use the resulting users table like any other table.