

Engineering
What Database Should You Use With Clerk? (2026)
Clerk gets sign-in working in an afternoon, then leaves you with the question its quickstart skips: where does everything else live, and how does that database know who is asking? Choose without a plan and you end up writing webhook sync code you didn't need, or shipping a Row-Level Security policy that quietly returns nothing because it reads a claim Clerk has since renamed.
What database should you use with Clerk?
Use Postgres. For most SaaS products the best single pairing is Clerk with Postgres on Supabase, which accepts Clerk session tokens natively so Row-Level Security can enforce each user's and organisation's access; choose Neon if only your server code touches the database, or Convex if you want a realtime TypeScript backend instead of SQL.
Clerk doesn't hold your application data and doesn't care which database you pick. It stores users, organisations, memberships and sessions; your tables store Clerk's IDs. What the choice really decides is where authorisation is enforced: by the database verifying Clerk's token itself (Supabase, Neon's Data API, Convex), or by your server code checking the session before every query (which works with any database at all).
How do Supabase, Neon, Convex, MongoDB and RDS compare with Clerk?
Prices are in USD as of October 2026, from each vendor's pricing page. Vendors change pricing, so check before you commit.
| Supabase | Neon | Convex | MongoDB Atlas | RDS or any Postgres | |
|---|---|---|---|---|---|
| How auth reaches the database | Clerk is a native third-party auth provider; RLS reads the session token | Server-side checks through an ORM; optional Data API verifies Clerk tokens via JWKS for RLS | First-party Clerk integration; checks live in your query and mutation functions | No Clerk integration; server-side checks only | Server-side checks; optional RLS from a per-request setting |
| Setup effort | Low: one setup step in each dashboard | Low with an ORM; moderate with the Data API | Low: activate in Clerk, add one config file | Low, but every query carries its own filter | Moderate: networking and pooling come first |
| Org multi-tenancy | RLS on the token's o.id claim | org_id filter in code, or RLS via the Data API | Org checks inside each function | orgId on every document, filtered in code | org_id plus RLS set from the session's org |
| Realtime | Yes, and Realtime honours RLS | Newer service, not yet in London | Yes: every query is reactive | No client realtime; build on change streams | None built in |
| Entry price | Free (2 active projects, 500 MB); Pro $25/month | Free (100 projects); Launch $0.106 per CU-hour, no minimum | Free; Professional $25 per developer/month | Free (512 MB); Flex $0.011/hour, capped at $30/month | Instance-hours plus storage; no permanent free tier |
| Lock-in | Low for data; higher with its API and Storage | Low | High: its own data model and functions, not SQL | Medium: MongoDB's query API | Lowest |
The first row is the decision. When the database verifies Clerk's token,
a query that forgets its filter can't leak data — Postgres won't return the
rows. When only your server checks the session, every query owns its org_id
filter, which is fine until someone forgets one. Supabase gives you the
database-enforced version with the least setup; on Neon and RDS you get the
same guarantee by setting the tenant per transaction, as shown below.
Realtime and lock-in pull in opposite directions. Convex's reactive queries are the best realtime developer experience here, but your data lives in its document model, not portable SQL. Supabase's Realtime sits on standard Postgres you can dump and move.
How does Clerk connect to your database?
Through IDs and tokens: your rows store Clerk's user and organisation IDs, and every request carries a short-lived session token saying who the user is and which organisation is active. Three mechanisms do the work.
IDs on your rows. Clerk IDs are prefixed strings — user_… and org_… —
so store them in text columns, not uuid. A created_by or org_id column
holding the Clerk ID is all most tables need.
The session token. Clerk's version 2 session token carries the user ID in
sub and the active organisation in a compact o object: o.id, o.rol
(role), o.slg (slug) and permissions. Version 1's separate org_id,
org_role and org_slug claims were deprecated on 14 April 2025, so treat
older tutorials that read org_id with suspicion. Keep custom claims small:
Clerk recommends under 1.2 KB, because the token travels in a cookie.
Webhooks. For changes outside a request — a user edits their email, an
admin removes a member — Clerk sends webhooks, delivered through Svix with
automatic retries. Most apps handle user.created, user.updated and
user.deleted, plus the matching organization.* and
organizationMembership.* created, updated and deleted events. Verify each
delivery with verifyWebhook() from @clerk/nextjs/webhooks.
Clerk's docs are candid that delivery isn't guaranteed, events can arrive out of order or twice, and synced data is eventually consistent. So write handlers as idempotent upserts keyed on the Clerk ID, and never make a synchronous flow such as onboarding wait for a webhook.
Do you need a users table if you use Clerk?
Often not at first: Clerk's docs recommend reading identity from the session token, which is always current, rather than maintaining a copy. If a request only needs to know who the current user is and which organisation they're acting for, the token already says so — no lookup, no sync, nothing to drift.
You need data about other users the moment you build a member list, an "assigned to" column, a mention picker or a report joining activity to people. There are two ways to get it:
- Call Clerk's Backend API. Fine for an admin screen or a short team list. Production instances allow 1,000 requests per 10 seconds and development instances 100, per Clerk's rate limits, so don't call it once per row of a table someone pages through.
- Keep a thin, webhook-synced copy. A
userstable keyed on the Clerk ID, with email, name and avatar, plus amembershipstable if you filter people by organisation. Now it's a SQL join — but it's a cache of Clerk, not the source of truth.
Our rule: start token-only, add the synced table when a feature first needs to
join on people, and also create each row lazily on the user's first
authenticated request, so a late user.created webhook never leaves anyone
without one. Owning user records outright is a different auth choice, weighed
in Clerk vs Better Auth.
How do you build multi-tenancy with Clerk Organizations?
Put a text org_id column on every tenant-owned table, fill it from the
active organisation in Clerk's session token, and let Row-Level Security reject
any row whose org_id doesn't match. Clerk Organizations supplies the rest:
the organisation switcher, invitations and roles. It's the shared-schema model
from our
multi-tenant SaaS architecture
guide, with Clerk's organisation ID as the tenant ID.
On Supabase, policies read Clerk's token directly through auth.jwt():
alter table projects enable row level security;
create policy "Members read their org's projects"
on projects for select to authenticated
using (org_id = (select auth.jwt()->'o'->>'id'));
create policy "Admins create projects in their org"
on projects for insert to authenticated
with check (
org_id = (select auth.jwt()->'o'->>'id')
and (select auth.jwt()->'o'->>'rol') = 'admin'
);
Three details catch people out. Version 2 roles drop the org: prefix, so the
check is 'admin', not 'org:admin'. If the user has no active organisation
the o claim is absent, the comparison yields null and the policy returns
nothing — the safe failure. And on the client, the Supabase SDK sends Clerk's
token when you pass a function returning session.getToken() as its
accessToken option; ignore any guide that has you copy your Supabase JWT
secret into a Clerk JWT template, an integration Supabase deprecated on 1 April
2025.
On Neon or RDS through an ORM, read orgId from Clerk's auth() helper on the
server and filter by it. For the same database-enforced guarantee, set it per
transaction with set_config('app.tenant_id', orgId, true) and write policies
against that setting. Neon's Data API can also verify Clerk tokens itself via
Clerk's JWKS URL, exposing claims to policies through auth.user_id() and
auth.session().
Budget for Clerk's side too: 100 monthly retained organisations are free per app; beyond that you need the $100/month Enhanced B2B add-on, then $1 per organisation a month.
Can Clerk integrate with an existing user database?
Yes, by importing your users into Clerk rather than connecting it to your
table: Clerk's Backend API accepts existing password hashes, so people keep
their passwords, and an external_id field preserves the link to your old user
IDs. Clerk becomes the system of record for identity; your application tables
stay where they are.
- Password hashes carry over. Clerk's create-user endpoint takes a password digest and the algorithm that produced it — bcrypt, argon2i and argon2id, several PBKDF2 variants, Firebase's scrypt, Django and phpass formats among them. Hashes from weak schemes such as MD5 and SHA-256 are upgraded to bcrypt transparently at each user's next successful sign-in.
- Old IDs keep working. Store each user's existing ID as
external_id, then customise the session token so a claim returns{{user.external_id || user.id}}. Migrated users carry their old ID, new users their Clerk ID, and your foreign keys don't change. - Bulk or trickle. Clerk's open-source migration script imports a JSON or CSV export through the Backend API within its rate limits. A trickle migration runs both systems side by side and moves users as they sign in, then bulk-imports anyone who never came back.
- Organisations are a separate job. Recreating organisations, memberships and roles is your own script against the Backend API.
Everyone signs in once more after cutover, with their old password. Budget a week or two, cutover included.
Our default: Next.js, Clerk and Postgres on Supabase
For the SaaS MVPs we build through our web development work, the stack is Next.js, Clerk, Postgres on Supabase, Stripe and Resend on Vercel, with Clerk connected as a Supabase third-party auth provider. Every tenant-owned table carries the Clerk organisation ID, and RLS policies read it straight from the session token, so isolation is the database's job rather than every query's. Jobs that genuinely cross tenants — billing, admin tools, migrations — use a privileged connection, kept to a small, reviewed corner of the codebase.
On cost, Supabase Pro is $25 a month, and Clerk users count as third-party MAUs with 100,000 included; the full bill is in Supabase pricing explained.
We deviate in two situations:
Neon, when the app only reaches its database from server-side code. A Next.js app on Clerk that queries through Drizzle or Prisma in server components and actions doesn't need Supabase's API, Storage or Realtime. Neon's scale-to-zero billing and per-PR branches fit that shape better, with tenancy enforced by per-transaction settings and RLS. The full comparison, including RDS for clients already on AWS, is in Neon vs Supabase vs AWS RDS.
Convex, when realtime is the product. Collaborative editors, live dashboards, anything multiplayer. Convex's Clerk integration is first-party, and its reactive queries remove a layer of WebSocket plumbing; Professional is $25 per developer a month, per Convex's pricing. The trade is a proprietary data model, harder reporting than SQL, and no London region: its EU deployments run in Ireland, where usage bills at 1.3x the US rate.
If enterprise SSO rather than speed is what's driving your auth decision, compare Clerk, WorkOS and Stytch before wiring anything to a database.
Frequently asked questions
Does Clerk work with MongoDB?
Yes, but only at the application level — Clerk has no MongoDB integration, so
your server verifies the session and adds the user or organisation filter to
every query. That works, but there's no database-level backstop like RLS: one
forgotten orgId filter returns another customer's documents. Atlas's free
tier is 512 MB, and Flex is capped at $30 a month. If you're weighing Atlas
against a Postgres backend, see
MongoDB Atlas vs Supabase.
Should you use Clerk's user ID as your primary key?
For a new build, yes — store Clerk's user and organisation IDs directly as
text keys rather than minting your own and mapping between them. RLS
policies can then compare columns straight to sub and o.id with no lookup.
The exception is an existing product: keep your IDs, store them as Clerk's
external_id and expose them in the token, as above.
Do Clerk users count towards Supabase's monthly active users?
Yes — users signing in through Clerk are billed as third-party MAUs, with 50,000 included on Free and 100,000 on Pro, then $0.00325 each. That's on top of Clerk's own bill, which counts monthly retained users: 50,000 included per app on both Hobby and Pro. At MVP scale neither number is close; at consumer scale, model both before you commit.
What database should a new SaaS startup choose?
Managed Postgres, on Supabase or Neon, unless your data is genuinely
document-shaped. SaaS data is relational — users, organisations,
subscriptions, invoices — and Postgres handles the flexible parts with jsonb.
Both hosts offer a free tier for validation and a London region, and moving
between them is a pg_dump away. The longer argument is in
Postgres vs MongoDB for a SaaS.
Want Clerk and your database wired up properly from day one?
Auth and data get connected in week one — token claims, webhooks, tenant columns and policies — and every later feature inherits those choices. To make them deliberately, book a free scoping call. We'll map the right stack to your product and quote the build fixed-price, so you know the cost before committing.





Leave a comment