Skip to content
Unverified — AI-generated content. Help verify this page

Supabase vs Firebase vs Appwrite vs PocketBase

Backend-as-a-Service (BaaS) platforms promise to eliminate the undifferentiated heavy lifting of building auth, databases, storage, and real-time APIs. But every BaaS makes trade-offs: relational vs document, open-source vs proprietary, managed vs self-hosted. This comparison dissects the four leading options across every dimension that matters.

Overview

PlatformFirst ReleaseDatabaseLicenseLanguage
Supabase2020PostgreSQLApache 2.0Elixir / TypeScript
Firebase2011 (acquired by Google 2014)Firestore (document) + RTDBProprietaryGo / Java (internal)
Appwrite2019MariaDB (internal)BSD-3PHP / Node.js
PocketBase2022SQLite (embedded)MITGo

The Fundamental Divide

Supabase and PocketBase give you a relational database with SQL. Firebase gives you a document store with its own query language. Appwrite sits in the middle with a document-like API backed by MariaDB. This distinction drives nearly every downstream difference.

Architecture Comparison

Feature Matrix

FeatureSupabaseFirebaseAppwritePocketBase
Database typePostgreSQL (relational)Firestore (document)MariaDB (relational, abstracted)SQLite (embedded relational)
Query languageSQL + PostgRESTFirestore SDK queriesREST / GraphQLREST + filter syntax
RealtimeWebSocket (Postgres CDC)Native (Firestore snapshots)WebSocket (Realtime API)SSE (Server-Sent Events)
Auth providers30+ OAuth + email/phone/SAML20+ OAuth + email/phone30+ OAuth + email/phoneOAuth2 + email
Row-Level SecurityPostgreSQL RLS (native SQL)Firestore Security RulesPermissions (role-based)Collection rules (API-level)
StorageS3-compatible, CDN, transformsGoogle Cloud StorageLocal / S3Local filesystem / S3
Image transformsResize, crop, format on-the-flyFirebase ExtensionsBuilt-in (limited)None (BYO)
Serverless functionsDeno Edge FunctionsCloud Functions (Node/Python)10+ runtimesGo hooks (embedded)
Database extensionspgvector, PostGIS, pg_cron, etc.None (proprietary)NoneNone
Full-text searchPostgreSQL FTSAlgolia integration neededBuilt-in (basic)SQLite FTS5
MigrationsSQL migrations CLIN/A (schemaless)Appwrite CLIAutomatic (schema changes)
Self-hostingDocker Compose (15+ containers)Not availableDocker Compose (1 container)Single binary (~15 MB)
Offline supportNot built-inFirestore offline persistenceNot built-inNot built-in
Multi-tenancySchema-per-tenant / RLSCollection-per-tenantProject-per-tenantCollection rules
Vector searchpgvector (native)Vertex AI integrationNoneNone
GraphQLpg_graphql extensionNone (REST only)Built-inNone
Pricing modelUsage-based + tierPay-per-read/write/storeFree (self-hosted)Free (self-hosted)

Code & Config Comparison

Client Initialization

Supabase:

typescript
import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  'https://xxx.supabase.co',
  'eyJ...' // anon key (safe for client)
);

Firebase:

typescript
import { initializeApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';

const app = initializeApp({
  apiKey: 'AIza...',
  authDomain: 'my-app.firebaseapp.com',
  projectId: 'my-app',
});
const db = getFirestore(app);

Appwrite:

typescript
import { Client, Databases } from 'appwrite';

const client = new Client()
  .setEndpoint('https://cloud.appwrite.io/v1')
  .setProject('my-project-id');

const databases = new Databases(client);

PocketBase:

typescript
import PocketBase from 'pocketbase';

const pb = new PocketBase('https://my-app.pockethost.io');

CRUD Operations

Supabase (SQL-based):

typescript
// Create
const { data, error } = await supabase
  .from('posts')
  .insert({ title: 'Hello', content: 'World', author_id: user.id })
  .select()
  .single();

// Read with joins
const { data: posts } = await supabase
  .from('posts')
  .select(`
    id, title, created_at,
    author:profiles(name, avatar_url),
    comments(id, body, created_at)
  `)
  .eq('published', true)
  .order('created_at', { ascending: false })
  .range(0, 9);

// Realtime subscription
const channel = supabase
  .channel('posts')
  .on('postgres_changes',
    { event: 'INSERT', schema: 'public', table: 'posts' },
    (payload) => console.log('New post:', payload.new)
  )
  .subscribe();

Firebase (document-based):

typescript
import {
  collection, addDoc, query, where,
  orderBy, limit, onSnapshot
} from 'firebase/firestore';

// Create
const docRef = await addDoc(collection(db, 'posts'), {
  title: 'Hello',
  content: 'World',
  authorId: user.uid,
  createdAt: serverTimestamp(),
});

// Read (no joins — denormalize or fan-out)
const q = query(
  collection(db, 'posts'),
  where('published', '==', true),
  orderBy('createdAt', 'desc'),
  limit(10)
);

// Realtime subscription
const unsubscribe = onSnapshot(q, (snapshot) => {
  snapshot.docChanges().forEach((change) => {
    if (change.type === 'added') {
      console.log('New post:', change.doc.data());
    }
  });
});

PocketBase:

typescript
// Create
const record = await pb.collection('posts').create({
  title: 'Hello',
  content: 'World',
  author: user.id,
});

// Read with expand (like joins)
const posts = await pb.collection('posts').getList(1, 10, {
  filter: 'published = true',
  sort: '-created',
  expand: 'author,comments_via_post',
});

// Realtime subscription
pb.collection('posts').subscribe('*', (e) => {
  console.log('Change:', e.action, e.record);
});

Security Rules

Supabase (PostgreSQL RLS):

sql
-- Enable RLS
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;

-- Users can read published posts
CREATE POLICY "Public read" ON posts
  FOR SELECT USING (published = true);

-- Users can only edit their own posts
CREATE POLICY "Owner update" ON posts
  FOR UPDATE USING (auth.uid() = author_id);

-- Users can insert with their own author_id
CREATE POLICY "Authenticated insert" ON posts
  FOR INSERT WITH CHECK (auth.uid() = author_id);

Firebase (Security Rules):

javascript
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /posts/{postId} {
      allow read: if resource.data.published == true;
      allow update: if request.auth.uid == resource.data.authorId;
      allow create: if request.auth.uid == request.resource.data.authorId;
    }
  }
}

Firestore Security Rules Gotcha

Firestore security rules cannot filter data — they only allow or deny entire document reads. You cannot write a rule like "only return fields X and Y." If a user can read a document, they read ALL fields. This has significant implications for data modeling.

Performance

Read Latency

OperationSupabase (managed)Firebase (Firestore)Appwrite (cloud)PocketBase (self-hosted)
Single row by PK5-15ms10-30ms10-25ms1-5ms (local SQLite)
List 100 rows15-40ms30-80ms20-50ms5-15ms
Complex join (3 tables)20-60msN/A (denormalized)N/A (no joins)10-30ms
Full-text search10-50ms (pg FTS)N/A (use Algolia)20-60ms5-20ms (FTS5)
Realtime delivery50-200ms20-100ms100-300ms50-150ms

Scaling Characteristics

DimensionSupabaseFirebaseAppwritePocketBase
Max connectionsDepends on plan (Supavisor pooler)Unlimited (managed)Depends on self-host config~10,000 concurrent
Max DB size8 GB (free) to unlimited1 GiB free, unlimited paidUnlimited (self-hosted)Limited by disk
Write throughputPostgreSQL limits (~10K TPS)~10K writes/sec per DBMariaDB limitsSQLite WAL (~1K TPS)
Horizontal scalingRead replicas, SupavisorAutomatic (Google infra)Manual (Docker scale)Not supported
Best for10K-1M users1M+ users (Google scale)1K-100K users1-10K users

Developer Experience

Strengths

Supabase:

  • SQL is a superpower — 50 years of tooling, knowledge, and optimization
  • Dashboard with table editor, SQL editor, and log explorer
  • supabase CLI for local dev with Docker (supabase start)
  • pgvector for AI/ML embedding search without another service

Firebase:

  • Offline-first with automatic sync (Firestore persistence)
  • Firebase Emulator Suite for full local development
  • Seamless integration with other Google Cloud services
  • Best-in-class mobile SDK (iOS, Android, Flutter)

Appwrite:

  • Fully self-hostable with a single docker compose up
  • 10+ function runtimes (Node, Python, Dart, Ruby, PHP, etc.)
  • Beautiful dashboard UI
  • No vendor lock-in by design

PocketBase:

  • Single binary, zero dependencies, starts in <1 second
  • Embed in Go applications as a library
  • Admin UI built-in
  • Perfect for prototypes, internal tools, and indie projects

Weaknesses

PlatformKey Limitation
SupabaseSelf-hosting is complex (15+ services); no native offline support
FirebaseNo SQL, no joins, no aggregations without Cloud Functions; vendor lock-in
AppwriteSmaller community; cloud offering is newer and less battle-tested
PocketBaseSingle-server only (no horizontal scaling); single maintainer project

When to Use Which

Decision Summary

ScenarioRecommended Platform
SaaS app with complex data relationshipsSupabase
Mobile app with offline-first requirementFirebase
Self-hosted backend, team wants full controlAppwrite
Weekend project, internal tool, prototypePocketBase
AI/ML app needing vector searchSupabase (pgvector)
Enterprise needing Google Cloud integrationFirebase
Startup wanting to avoid vendor lock-inSupabase or Appwrite
Indie hacker shipping fast, soloPocketBase

Migration

Firebase to Supabase

Supabase provides an official migration tool:

bash
# 1. Export Firestore data
npx firestore-export --accountCredentials serviceAccount.json \
  --backupFile firestore-export.json

# 2. Transform document data to relational schema
# This is the hard part — you must design proper tables
# and normalize the denormalized Firestore documents

# 3. Create tables in Supabase
psql "postgresql://postgres:password@db.xxx.supabase.co:5432/postgres" \
  -f schema.sql

# 4. Import data
# Use Supabase's CSV import or write a migration script

# 5. Migrate auth users
# Supabase provides a Firebase Auth migration guide
# that preserves user IDs and password hashes

# 6. Update client code
# Replace Firebase SDK calls with Supabase SDK calls
# Main change: document queries → SQL-like queries

Firebase Migration Complexity

Migrating from Firestore to a relational database is not a simple data export. You must redesign your data model from denormalized documents to normalized tables. Budget 2-6 weeks for a production migration depending on data complexity.

Supabase to Self-Hosted

bash
# 1. Clone the Supabase Docker setup
git clone https://github.com/supabase/supabase
cd supabase/docker

# 2. Configure environment
cp .env.example .env
# Edit .env with your secrets

# 3. Export data from managed Supabase
pg_dump "postgresql://postgres:password@db.xxx.supabase.co:5432/postgres" \
  --no-owner --no-acl > backup.sql

# 4. Start self-hosted Supabase
docker compose up -d

# 5. Restore data
psql "postgresql://postgres:password@localhost:5432/postgres" \
  -f backup.sql

# 6. Update client to point to self-hosted URL

Verdict

Supabase is the best general-purpose BaaS for web applications. PostgreSQL gives you the full power of SQL, joins, extensions (pgvector, PostGIS), and 50 years of ecosystem. The managed offering is mature, and the open-source nature means you always have an exit path.

Firebase remains the king of mobile development. Its offline persistence, real-time sync, and deep Google Cloud integration make it the natural choice for iOS/Android apps. The cost of Firestore's document model is the inability to do relational queries — you pay for this with denormalization and data duplication.

Appwrite is the strongest choice for teams that want a self-hosted BaaS with a polished experience. Its multi-runtime function support and beautiful dashboard set it apart, though the community is smaller than Supabase or Firebase.

PocketBase is a revelation for solo developers and small projects. A single 15 MB binary that includes auth, database, file storage, and an admin UI is remarkable. It will not scale to millions of users, but for internal tools, prototypes, and indie projects it is hard to beat.

Bottom Line

If you are building a web SaaS, start with Supabase. If you are building a mobile app that must work offline, start with Firebase. If you want to self-host everything in one docker compose up, choose Appwrite. If you are a solo developer who wants to ship this weekend, grab PocketBase.

Which Would You Choose?

Scenario 1: You are building a SaaS analytics dashboard. You need complex SQL joins between users, organizations, projects, and events. Your team has strong SQL skills.

Recommendation: Supabase

Relational data with complex joins is PostgreSQL's sweet spot. Supabase gives you full SQL power, Row-Level Security for multi-tenancy, and pgvector if you later add AI features. Firebase's document model would force you to denormalize everything, making analytics queries painful or impossible.

Scenario 2: You are building a real-time collaborative mobile app (like a chat or whiteboard) that must work offline on iOS and Android. Users in rural areas may have intermittent connectivity.

Recommendation: Firebase

Firebase's offline persistence and real-time sync are unmatched. Firestore automatically caches data locally, queues writes when offline, and syncs when connectivity resumes. No other BaaS handles the offline-first mobile use case this well. The tradeoff of a document model is worth it for this scenario.

Scenario 3: You are a solo developer building an internal tool for your 50-person company. Budget is zero, and you have a spare Linux server.

Recommendation: PocketBase

A single 15 MB binary with built-in auth, SQLite database, file storage, and admin UI. Download it, run it, and you are done. No Docker, no cloud accounts, no billing surprises. PocketBase is purpose-built for exactly this scenario.

Common Misconceptions

  • "Supabase is just Postgres with a UI" — Supabase includes GoTrue for auth (30+ OAuth providers), a real-time WebSocket server, S3-compatible storage with on-the-fly image transforms, Edge Functions on Deno, and pgvector for AI embeddings. It is a full BaaS platform.
  • "Firebase locks you in forever" — While migration is non-trivial (document-to-relational conversion), Firebase's data can be exported. The real lock-in is the Firestore query model and Security Rules, not the data itself.
  • "PocketBase cannot scale" — PocketBase handles ~10,000 concurrent users on modest hardware. Most internal tools and indie projects never reach this limit. Scale is only a concern if you are building the next Twitter.
  • "Appwrite is just a worse Supabase" — Appwrite supports 10+ function runtimes (including Dart for Flutter teams), has a polished self-hosted dashboard, and is fully BSD licensed. It serves a different niche: teams who want total control over their backend.

Real Migration Stories

Keel.so: Firebase to Supabase — Keel, a backend platform, migrated from Firestore to Supabase when their data model became too relational for a document database. The biggest challenge was converting denormalized Firestore documents into normalized PostgreSQL tables, which took 4 weeks of data modeling and migration scripting.

Plausible Analytics: Self-hosted approach — Plausible chose to build on PostgreSQL (ClickHouse for analytics) rather than any BaaS because they needed full data sovereignty for their privacy-focused analytics product. Their experience shows that for data-sensitive products, self-hosting (whether with Supabase Docker or raw Postgres) can be a competitive advantage.

Quiz

1. What is the fundamental data model difference between Supabase and Firebase?

Supabase uses PostgreSQL (relational, SQL, joins, foreign keys). Firebase uses Firestore (document-based, NoSQL, denormalized, no joins). This difference drives nearly every downstream trade-off.

2. Why can Firestore Security Rules NOT replace Row-Level Security in Supabase?

Firestore Security Rules cannot filter data — they only allow or deny entire document reads. If a user can read a document, they read ALL fields. PostgreSQL RLS can filter rows and columns with arbitrary SQL expressions, providing much finer-grained access control.

3. What makes PocketBase unique among these four BaaS options?

PocketBase is a single ~15 MB binary with zero dependencies. It embeds SQLite, auth, file storage, and an admin UI in one executable. No Docker, no cloud services, no multi-service orchestration.

4. When does Firebase's offline persistence give it an advantage over Supabase?

When building mobile apps for users with intermittent connectivity. Firestore automatically caches data locally, queues writes offline, and syncs when connectivity returns. Supabase has no built-in offline support.

5. Why might you choose Appwrite over Supabase for a self-hosted deployment?

Appwrite self-hosts with a single docker compose up (one container), while Supabase self-hosting requires 15+ containers. Appwrite also supports 10+ function runtimes (Node, Python, Dart, Ruby, PHP, etc.), making it more flexible for polyglot teams.

One-Liner Summary

Supabase gives you PostgreSQL superpowers for web SaaS, Firebase owns offline-first mobile, Appwrite is the self-hosting champion, and PocketBase is the solo developer's dream.

"What I cannot create, I do not understand." — Richard Feynman