Skip to content
Tech News
← Back to articles

From Supabase to Clerk to Better Auth

read original get Supabase Authentication Starter Kit → more articles
Why This Matters

This article highlights the challenges and industry implications of choosing third-party authentication solutions, emphasizing the importance of reliable, scalable user management for developers and businesses. The switch from Clerk to Better Auth underscores the need for robust security and architecture compatibility in modern web applications.

Key Takeaways

In 2023, I wrote about how Val Town moved away from Supabase and toward a more conventional database setup. We were using a lot of Supabase's functionality, including their authentication. So when it came time to move we found equivalents: Render for the database, and Clerk for authentication. But life came at us fast, and by late 2023 we had an issue filed: get off of Clerk. That issue was finally closed a month ago, when we switched to Better Auth.

Some important context is that Clerk is a major success. They just raised 50 million dollars and they have lots of satisfied users. Heck, in related news Supabase raised 100 million dollars at a 5 billion dollar valuation. Congratulations to both of them. I would make a terrible venture capitalist. Whatever opinions and experiences I hold about authentication and row level security are secondary to these numbers and proof. You can't argue with success.

But still, I am happy to have closed that issue and switched to Better Auth. It's been a tough experience, with a lot of workarounds, bugs, and outages. The architecture of Val Town sharply conflicted with Clerk's expectations.

The core issue

The core issue is that Clerk tried to be your users table and your sessions table. I think they've been shifting away from saying this, but it started from a pretty extreme place: there's a 2021 blog post titled "Consider dropping your users table". There's a YouTube video from 2023 called DELETE your Users table. I strongly suggest you don't!

There are two big problems with farming out your users table to a third-party service.

Clerk was a pretty bad replacement for a users table because it was heavily rate-limited and not very reliable. When we initially switched, I assumed we could load user data from Clerk's API whenever we needed to. After all, we want to know things like user settings, avatar URLs, and emails. Clerk's SDK made this pretty convenient: the rootAuthLoader, the thing that handles auth for your whole application, had a nice little option called loadUser which would do the request for you. Worked great in development. In production, the rate limit for that endpoint was five requests per second. For the whole account, across all users. Tough! A pretty bad footgun, that we discovered in production, and was eventually fixed by removing the option.

The rate limiting hit us especially hard for the social aspect of Val Town. For example, let's say you have a social website, so a lot of pages have lists of content from other users, and usernames and avatars to identify them. Clerk's default UIs are based on the assumption that a user only sees their own avatar and needs their own settings and information, and they can get all of that stuff from their nifty JWT token. Social websites like Val Town completely break this assumption, and the advice was to sync avatars and other information between Clerk and our users table: so now we have two authorities for that information, and the complexity of two users tables.

So we had to sync Clerk's data to our database by using webhooks, which meant that signing up was convoluted and tricky - for a few moments, a user has a Clerk account but no Val Town database row. Or, because our platform requires usernames, users can be in a state where they have a Clerk account, a database row, but their account is incomplete. Our user settings had to be split between things that Clerk controlled, like auth strategies, and things that we needed, like usernames and editor settings.

The second is that Clerk became a single point of failure for all our user sessions. Cookie-based user sessions are usually short-lived with constant refreshing: that way they can be invalidated quickly. But that also means that every few minutes users need to swap their session cookies for new ones. So when someone's login session needed to be refreshed, it was a subdomain of Val Town that passed the request to Clerk that did the refreshing. We didn't have a sessions table or any responsibility over sessions.

... continue reading