The long answer is the rest of this post. The two backends made opposite bets about how your data should be shaped. That one choice leaks into your auth, your bill, and how hard it is to leave later.

I've shipped four consumer apps to production on Supabase over the last year. I picked it on purpose, and I've also hit its sharp edges, so this is a comparison written from scars, not from a features page.
The data model is the actual decision
Everything else follows from one choice: relational or document. Supabase is managed Postgres, so your data is tables with real foreign keys, joins, and transactions. Firebase's Firestore is a NoSQL document store, so queries are shallow and cannot traverse relationships without extra reads or denormalized copies.
Here is what that difference feels like in code. In Supabase you ask for a user and their posts in one query:
select users.name, posts.title
from posts
join users on users.id = posts.author_id
where users.id = '...';
In Firestore you fetch the user, then loop and fetch each related document, or you duplicate the user's name onto every post so you never have to join. Both work. One of them turns into a maintenance tax as your data grows.
Firebase noticed this pain and shipped Data Connect (now Firebase SQL Connect), a managed Cloud SQL Postgres service with a GraphQL layer on top. It closes the "Firebase cannot do joins" gap. The catch: you're consuming a Google-managed endpoint through Firebase APIs, not owning a Postgres database the way you do on Supabase.
Auth and access control: RLS versus Security Rules
Both platforms give you email, Google, GitHub, Apple, and phone sign-in out of the box. The real difference is where you write your access rules.
Supabase puts them in the database with Postgres Row Level Security. A policy is SQL, and it runs no matter which client hits the table:
create policy "users read own rows"
on documents for select
using ( auth.uid() = owner_id );
Firebase puts them in a separate rules file that guards the Firestore API:
match /documents/{id} {
allow read: if request.auth.uid == resource.data.ownerId;
}
Neither is wrong. RLS keeps the rule next to the data, which I like because there is one source of truth. Firebase's rules are easier to read on day one but live away from your schema. Both platforms also support anonymous auth for "try before you sign up" flows. On Supabase, anonymous users even carry an is_anonymous claim you can gate an RLS policy on.
Pricing: flat-ish versus pay-per-read
This is where teams get surprised. Supabase is mostly a flat subscription. Firebase is metered by usage, and metered bills are the ones that spike.
Supabase gives you two free projects (500 MB database, 50,000 monthly active users). Then the Pro plan is 25 dollars a month, with a 10 dollar compute credit that covers a small instance. Most early apps sit at exactly 25 dollars until they scale.
Firebase's Spark plan is free with daily caps (50,000 Firestore reads, 20,000 writes). Above that you move to Blaze, which bills per operation: roughly 0.03 dollars per 100,000 reads and 0.09 dollars per 100,000 writes, with prices varying by region. Reads are the cheap part. That still sounds tiny until a chatty client re-reads a collection on every screen and your read count runs away from you.
The mental model: Supabase costs are predictable and driven by compute and egress. Firebase costs are driven by how your app reads and writes, which is easy to underestimate before launch.
One Supabase gotcha you must know: free projects pause after about a week of inactivity, and a paused project's subdomain stops resolving. It bit me once on a low-traffic app. Keep a free project warm with a scheduled ping, or put anything real on Pro.
AI and vector search
If you're building anything with embeddings, Supabase has the cleaner story. Its pgvector support means your embeddings, your application data, and your access policies all live in the same Postgres database, and you query them with plain SQL:
select id, content
from docs
order by embedding <-> '[0.1,0.2,0.3]'::vector
limit 5;
That is RAG without a second datastore to sync. Firebase answers with AI Logic for Gemini integration and vector search through extensions, which works but keeps the pieces more separate. For AI-heavy apps, "same database" is a real advantage.
Lock-in: how hard is it to leave
Supabase is open source and runs on standard Postgres, so leaving is recoverable in weeks. You can even self-host the whole stack. Firebase is Google-managed with proprietary APIs, so leaving is structural and closer to months of work. If optionality matters to you, weight this heavily.
So which one?
Choose Supabase when your data is relational, you want full SQL, you're adding AI or vector features, or you want the option to self-host. Choose Firebase when you're building an offline-first mobile app, or you want realtime sync and client caching with almost no setup. It also fits if you already live in Google's stack with Analytics, Crashlytics, and FCM.
For my apps the deciding factors were relational data and pgvector, so Supabase won. If I were shipping a purely offline-first app tomorrow, I'd give Firebase an honest look for its client-side sync alone. The best backend is the one whose default shape matches your app's default shape.
I write these from real work at astraedus.dev, where I build apps and tools. Building something, or stuck on something like this? Reach me at astraedus.dev or [email protected].
Get the next one in your inbox → subscribe at astraedus.dev.