I've been building with Firebase for years. It's the backend behind something like 90% of my personal projects, from weekend MVPs to apps with real users: Auth, Firestore, Cloud Functions, Storage. It all fits with the Google tooling I already know, and it mostly just works.
Lately I've been spending time with Supabase, and it's impressed me. I like PostgreSQL a lot, so a BaaS built entirely on Postgres was always going to get my attention. The developer experience is clean, performance is solid, and the SQL-first approach feels right for a certain kind of project.
I still reach for Firebase more often, though. Not because Supabase falls short, but because Firebase fits my workflow and the kind of apps I usually build. This post is my attempt at a fair technical comparison: NoSQL versus SQL, the core features side by side, pricing, and why I end up where I do.
The real difference is the data model
Everything else follows from this. Firebase gives you a document database; Supabase gives you Postgres.
Firestore: documents and collections
Firestore stores JSON-like documents grouped into collections:
// Users collection
users/{userId} {
name: "Sebastian",
email: "sebastian@example.com",
createdAt: timestamp,
settings: {
theme: "dark",
notifications: true
}
}
// Posts subcollection
users/{userId}/posts/{postId} {
title: "My First Post",
content: "...",
tags: ["tech", "firebase"],
likes: 42
}
There's no fixed schema, so you add fields whenever you need them. You denormalize a lot to avoid joins, documents can nest maps and arrays, and collection group queries let you search across every subcollection with the same name.
Querying used to be Firestore's weak spot. In January 2026 Google announced pipeline operations for the new Firestore Enterprise edition: over a hundred new query capabilities, including aggregations, unnesting arrays and regex matching, plus indexes that are optional instead of mandatory. It launched in preview and went generally available at Cloud Next in April 2026. This is the kind of thing that's now possible:
// Top 10 tags across all recipes
const snapshot = await db.pipeline()
.collection("recipes")
.select("tags")
.unnest(field("tags").as("tagName"))
.aggregate({
accumulators: [countAll().as("tagCount")],
groups: ["tagName"]
})
.sort(field("tagCount").descending())
.limit(10)
.execute();
It doesn't turn Firestore into a relational database, but it closes a lot of the gap with other NoSQL databases.
Supabase: plain PostgreSQL
Supabase is Postgres with a lot of tooling around it. You define your schema up front:
-- Users table
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Posts table with a foreign key
CREATE TABLE posts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
title TEXT NOT NULL,
content TEXT,
tags TEXT[],
likes INTEGER DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_posts_user_id ON posts(user_id);
You get normalized data with foreign keys and joins, ACID transactions, the whole SQL language (CTEs, window functions and so on) and Postgres extensions like PostGIS for geo data or pgvector for embeddings.
When documents make more sense
Firestore shines when you want to prototype without designing a schema, when your data is naturally hierarchical (a user profile with nested settings), when the shape changes often, when the app is mobile-first and needs to work offline, and for real-time things like chat, presence or live cursors.
A social feed is a good example:
// Post with embedded author and comments (denormalized)
posts/{postId} {
author: { id: "...", name: "...", avatar: "..." },
content: "...",
comments: [
{ userId: "...", text: "...", timestamp: ... },
{ userId: "...", text: "...", timestamp: ... }
],
likes: ["userId1", "userId2", "userId3"]
}
You copy the author's data into every post, and in exchange a single document read gives you everything you need to render it. The price shows up later, when that author changes their avatar and you have to update every copy.
When tables make more sense
Postgres wins when the data has lots of relationships (products, orders, inventory), when integrity really matters (anything with money), when you need reporting and aggregations, when your team already knows SQL, and whenever duplicating data would cause more problems than it solves.
SELECT
o.id AS order_id,
u.name AS customer_name,
p.name AS product_name,
oi.quantity,
oi.price,
(oi.quantity * oi.price) AS total
FROM orders o
JOIN users u ON o.user_id = u.id
JOIN order_items oi ON oi.order_id = o.id
JOIN products p ON oi.product_id = p.id
WHERE o.status = 'completed'
AND o.created_at > NOW() - INTERVAL '30 days';
In Firestore this means several reads and stitching the results together on the client. In Postgres it's just a query.
Feature by feature
Authentication
Firebase Auth supports email and password, Google, Facebook, X, GitHub, Apple, phone, anonymous sign-in and custom tokens. It has drop-in UI for web and mobile and ties straight into Firestore security rules. SAML and OIDC need the upgrade to Identity Platform.
import { signInWithPopup, GoogleAuthProvider } from 'firebase/auth';
const provider = new GoogleAuthProvider();
const result = await signInWithPopup(auth, provider);
const user = result.user;
Supabase Auth covers email and password, magic links, OAuth providers and phone. Users live in an auth.users table, SAML SSO is available on the paid plans without a separate product, and access control is done with Row Level Security policies in Postgres.
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'google'
});
I'd call this one a tie. Firebase's SDKs and UI pieces are more polished; RLS is a really powerful way to express complicated authorization rules.
Database
Firestore is managed and scales automatically, has offline support with automatic sync built in, and lets you subscribe to documents, collections and queries in real time.
Supabase gives you Postgres with dozens of available extensions (PostGIS, pgvector, pg_cron and more), auto-generated REST and GraphQL APIs from your schema, and real-time change events through Postgres logical replication. You scale it vertically, with read replicas on the paid plans.
Which one wins depends entirely on the app: Firestore for mobile with offline needs, Supabase for relational data and heavy queries.
Storage
Cloud Storage for Firebase sits on top of Google Cloud Storage, uses Firebase Auth and security rules for access, and handles resumable uploads for large files. You pay separately for storage, bandwidth and operations.
Supabase Storage is S3-backed, keeps file metadata in Postgres so RLS policies apply to files too, supports resumable uploads (TUS), and offers on-the-fly image transformations on the paid plans. The upload limit is 50 MB per file on the free plan and up to 500 GB on Pro.
Both are good here. Firebase has the longer track record with heavy media apps; Supabase's RLS integration is the more elegant model.
Functions
Cloud Functions for Firebase run Node.js (or Python), triggered by HTTP, Firestore, Auth, Storage, Pub/Sub or a schedule. They're deeply wired into the rest of Firebase. Cold starts can hurt, though the second generation is better. You pay per invocation plus compute time.
import { onDocumentCreated } from 'firebase-functions/v2/firestore';
export const onUserCreate = onDocumentCreated('users/{userId}', async (event) => {
const userData = event.data.data();
await sendEmail(userData.email, 'Welcome!');
});
Supabase Edge Functions run on Deno with TypeScript, deploy globally close to your users and talk to Postgres directly. Cold starts are typically faster than traditional serverless, and the Pro plan includes 2 million invocations a month.
import { createClient } from 'npm:@supabase/supabase-js@2';
Deno.serve(async (req) => {
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_ANON_KEY')!
);
const { data } = await supabase.from('users').select('*');
return new Response(JSON.stringify(data), {
headers: { 'Content-Type': 'application/json' }
});
});
Firebase wins on ecosystem; Supabase on edge latency. And I like Deno, so that's a point in its favor for me.
Real-time
Firestore (and the older Realtime Database) let you listen to documents, collections and queries. Changes made offline get queued and synced when the connection comes back, and presence is easy to build.
import { doc, onSnapshot } from 'firebase/firestore';
const unsubscribe = onSnapshot(doc(db, 'posts', postId), (snap) => {
console.log('Current data:', snap.data());
});
Supabase Realtime has three pieces: Postgres Changes (listen to inserts, updates and deletes on a table), Broadcast (pub/sub for ephemeral messages) and Presence. The free plan allows 200 concurrent connections, Pro 500.
const channel = supabase
.channel('posts')
.on('postgres_changes',
{ event: '*', schema: 'public', table: 'posts' },
(payload) => console.log('Change:', payload)
)
.subscribe();
Firebase wins this one for me. Supabase Realtime is solid, but it doesn't cache offline, and for mobile apps Firestore's offline sync is hard to beat.
When I pick each one
Firebase
Mobile-first apps that need to work offline: notes, messaging, anything where the connection is unreliable. MVPs I want to ship in a weekend without designing a schema. Projects already using Google Cloud, Analytics or Workspace APIs. And real-time collaboration, where Firestore listeners make presence and live updates almost trivial.
A real-time chat is a good example of how little code it takes:
const messagesRef = collection(db, 'rooms', roomId, 'messages');
const q = query(messagesRef, orderBy('timestamp', 'desc'), limit(50));
onSnapshot(q, (snapshot) => {
const messages = snapshot.docs.map((d) => ({
id: d.id,
...d.data()
}));
setMessages(messages);
});
It works offline, syncs on its own, and there's no backend for you to run.
Supabase
Relational data with lots of joins: e-commerce, SaaS dashboards, CRMs. Analytics and reports where you want real SQL. AI work with pgvector for semantic search, embeddings or RAG. And sometimes I just want Postgres, and Supabase gives it to me with a nice developer experience.
Semantic search, for example, is one query:
-- Semantic search with pgvector
SELECT
id,
title,
content,
1 - (embedding <=> query_embedding) AS similarity
FROM documents
WHERE 1 - (embedding <=> query_embedding) > 0.7
ORDER BY similarity DESC
LIMIT 10;
Firestore does have vector search now, but having it next to the rest of your relational data, in the same query language, is what makes it nice in Postgres.
Performance
Firestore scales automatically to a huge number of concurrent users, replicates across regions and serves reads instantly from the local cache when offline. Its weak points are the ones you'd expect: complex queries are still its hardest case, joins happen on the client, and keeping denormalized copies in sync costs writes.
Postgres is fast when you index properly (B-tree, GiST, GIN), joins are native, and the connection pooler handles lots of clients. On the other hand there's no offline mode, you'll eventually need read replicas, and connection setup isn't free.
In practice, simple reads feel comparable on both. Where Postgres pulls clearly ahead is joins and aggregations, which is exactly where Firestore makes you do work on the client.
Pricing
Numbers as of this writing; check the pricing pages before you budget anything.
Firebase
The free Spark plan gives you 1 GiB of Firestore storage, 50,000 reads, 20,000 writes and 20,000 deletes per day, and 10 GiB of egress a month.
On the pay-as-you-go Blaze plan, Firestore Standard in us-central1 costs about $0.03 per 100,000 reads, $0.09 per 100,000 writes and $0.01 per 100,000 deletes, with egress at $0.12/GiB after the free 10 GiB. Cloud Functions are $0.40 per million invocations plus compute.
The catch: you pay per operation, so a listener on a busy collection or a sudden spike in traffic can show up on the bill in ways you didn't expect.
Supabase
The free plan includes a 500 MB database, 1 GB of file storage, 5 GB of egress, 50,000 monthly active users and 200 realtime connections.
Pro starts at $25 a month with an 8 GB database, 100 GB of storage, 250 GB of egress, 100,000 MAU, 500 realtime connections and daily backups, with usage-based charges above that. Team starts at $599 a month.
The catch: the database size limits can get tight for data-heavy apps.
For small projects both free tiers are generous. At scale, I find Supabase easier to predict, because you're paying for resources instead of every single read and write.
Why I still lean Firebase
I'll admit it: I'm biased. A few reasons.
I've used Google Cloud for years. I know the console, IAM, the monitoring tools, and Firebase slots right into that.
Almost all my personal projects use Firebase for auth, data or storage, so the muscle memory is real.
A lot of what I build is mobile or needs to work offline, and Firestore's automatic sync is something I can't easily replace.
And I already use Analytics, Cloud Run and Pub/Sub. Having everything in one place makes billing, monitoring and debugging simpler.
That said, Supabase has won me over for specific things: projects where I want SQL, AI work with pgvector, open source projects where self-hosting matters, and anything analytics-heavy. Seeing Postgres packaged as a BaaS with this good a developer experience is exciting.
So which one?
Go with Firebase if the app is mobile-first and needs offline, if you want to prototype without designing a schema, if you already live in Google's ecosystem, if you need real-time collaboration, or if you're comfortable with NoSQL and denormalized data.
Go with Supabase if your data is relational and complex, you need serious SQL, you want open source and the option to self-host, you're building AI features with pgvector, or you simply prefer Postgres and normalized data.
Or use both. Nothing stops you from letting Firebase handle auth and real-time while Supabase handles relational data and analytics, each doing what it's best at.
Neither is "better" in general. It's about your use case, what your team knows and how your architecture looks. For me, Firebase stays the default because of familiarity, ecosystem and the kind of apps I make. Supabase has earned a lot of respect, especially for anything Postgres-heavy and for AI work.
Try both, build something small with each, and keep the one that fits.
Have you used both? Switched from one to the other? I'd like to hear what pushed you either way.
What did you think?
Found this article helpful? Share it!
Help others discover this content by sharing it on your favorite platform.



