DEV Community

Otto
Otto

Posted on

Supabase in 2026: The Open-Source Firebase That Actually Scales

Supabase in 2026: The Open-Source Firebase That Actually Scales

Firebase promised simplicity. For small apps, it delivered. Then your app grew, your Firestore queries got complex, and you hit the NoSQL wall.

Supabase is what Firebase should have been — backed by PostgreSQL, fully open-source, and increasingly the default choice for new projects in 2026.

Here's everything you need to know.

What Is Supabase?

Supabase is an open-source Firebase alternative built on top of PostgreSQL. It gives you:

  • PostgreSQL database (the most powerful open-source database)
  • Auto-generated REST and GraphQL APIs
  • Authentication (email, OAuth, passwordless, phone)
  • Real-time subscriptions (via WebSockets)
  • Storage (file uploads, image transformations)
  • Edge Functions (Deno-based serverless functions)
  • Vector database (pgvector for AI applications)

All of this, self-hostable or on their managed cloud.

Why PostgreSQL Changes Everything

The killer advantage of Supabase is the underlying database. With Firebase/Firestore:

// Firebase — "just" get posts by user with a tag
// Requires a compound query... that you must pre-configure an index for
const q = query(
  collection(db, 'posts'),
  where('userId', '==', userId),
  where('tags', 'array-contains', 'svelte'),
  orderBy('createdAt', 'desc'),
  limit(10)
);
// Error: Index required. Build it first.
Enter fullscreen mode Exit fullscreen mode

With Supabase/PostgreSQL:

// Supabase — same query, no setup needed
const { data } = await supabase
  .from('posts')
  .select('*')
  .eq('user_id', userId)
  .contains('tags', ['svelte'])
  .order('created_at', { ascending: false })
  .limit(10);
Enter fullscreen mode Exit fullscreen mode

PostgreSQL can join tables, run complex aggregations, and handle queries that would be impossible or prohibitively expensive in NoSQL. You gain 40 years of database engineering.

Getting Started: Your First Supabase App

1. Setup

npm install @supabase/supabase-js
Enter fullscreen mode Exit fullscreen mode
// lib/supabase.js
import { createClient } from '@supabase/supabase-js';

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;

export const supabase = createClient(supabaseUrl, supabaseKey);
Enter fullscreen mode Exit fullscreen mode

2. Authentication

// Sign up
const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'securepassword'
});

// Sign in
const { data, error } = await supabase.auth.signInWithPassword({
  email: 'user@example.com',
  password: 'securepassword'
});

// OAuth (GitHub, Google, etc.)
const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'github'
});

// Get current user
const { data: { user } } = await supabase.auth.getUser();
Enter fullscreen mode Exit fullscreen mode

3. Database CRUD

// Create
const { data, error } = await supabase
  .from('posts')
  .insert({ title: 'My First Post', content: 'Hello world', user_id: user.id })
  .select();

// Read
const { data } = await supabase
  .from('posts')
  .select(`
    id,
    title,
    content,
    created_at,
    profiles (username, avatar_url)
  `)
  .order('created_at', { ascending: false });

// Update
const { data } = await supabase
  .from('posts')
  .update({ title: 'Updated Title' })
  .eq('id', postId)
  .eq('user_id', user.id); // security: user can only update their own posts

// Delete
const { error } = await supabase
  .from('posts')
  .delete()
  .eq('id', postId);
Enter fullscreen mode Exit fullscreen mode

4. Row Level Security (The Magic Part)

RLS is PostgreSQL policies that run on every query. You define them once in SQL, and they apply everywhere — REST API, GraphQL, realtime subscriptions:

-- Anyone can read posts
CREATE POLICY "Posts are public" 
ON posts FOR SELECT 
USING (true);

-- Only owner can insert/update/delete
CREATE POLICY "Users manage own posts" 
ON posts FOR ALL 
USING (auth.uid() = user_id);
Enter fullscreen mode Exit fullscreen mode

Now the JavaScript code from section 3 is secure by default. Even if a client bypasses your API and calls Supabase directly, they can only touch their own data.

This is the correct way to handle authorization. Not middleware. Not code. Database policies.

Real-Time Subscriptions

// Listen for new messages in a chat room
const channel = supabase
  .channel('messages')
  .on(
    'postgres_changes',
    { event: 'INSERT', schema: 'public', table: 'messages', filter: `room_id=eq.${roomId}` },
    (payload) => {
      setMessages(prev => [...prev, payload.new]);
    }
  )
  .subscribe();

// Cleanup
return () => supabase.removeChannel(channel);
Enter fullscreen mode Exit fullscreen mode

This works via PostgreSQL's logical replication. No extra infrastructure needed.

Storage

// Upload a file
const { data, error } = await supabase.storage
  .from('avatars')
  .upload(`${user.id}/avatar.jpg`, file);

// Get public URL
const { data } = supabase.storage
  .from('avatars')
  .getPublicUrl(`${user.id}/avatar.jpg`);

// Image transformation (resize on the fly)
const { data } = supabase.storage
  .from('avatars')
  .getPublicUrl('user123/photo.jpg', {
    transform: {
      width: 200,
      height: 200,
      resize: 'cover'
    }
  });
Enter fullscreen mode Exit fullscreen mode

Vector Search (AI Applications)

Supabase added pgvector support in 2024. In 2026, it's the standard way to build semantic search:

-- Enable pgvector
CREATE EXTENSION IF NOT EXISTS vector;

-- Table with embeddings
CREATE TABLE documents (
  id bigserial primary key,
  content text,
  embedding vector(1536) -- OpenAI text-embedding-3-small dimensions
);
Enter fullscreen mode Exit fullscreen mode
// Store an embedding
const embedding = await openai.embeddings.create({
  model: 'text-embedding-3-small',
  input: document.content
});

await supabase.from('documents').insert({
  content: document.content,
  embedding: embedding.data[0].embedding
});

// Semantic search
const { data } = await supabase.rpc('match_documents', {
  query_embedding: queryEmbedding,
  match_threshold: 0.7,
  match_count: 10
});
Enter fullscreen mode Exit fullscreen mode

Supabase vs Firebase in 2026: Final Verdict

Feature Supabase Firebase
Database PostgreSQL (relational, powerful) Firestore (NoSQL, limited queries)
Pricing More generous free tier Can get expensive fast
Open Source Yes, self-hostable No
Real-time Yes (WebSockets) Yes (WebSockets)
Auth Full-featured Full-featured
Vendor lock-in Low (standard Postgres) High
AI/Vector Yes (pgvector) Vertex AI (complex)
Learning curve Moderate (SQL helps) Easy

Choose Supabase when: You need relational data, want to avoid vendor lock-in, or are building AI features.

Choose Firebase when: You need maximum simplicity and your team doesn't know SQL.

Free Tier in 2026

Supabase free tier includes:

  • 2 projects (paused after 1 week of inactivity)
  • 500MB database
  • 1GB file storage
  • 50,000 monthly active users
  • 500K Edge Function invocations

For a side project or MVP, this is more than enough.

Quick-Start Checklist

  1. Create account at supabase.com
  2. Create new project
  3. Run migrations (use the SQL editor or Supabase CLI)
  4. Enable RLS on every table (ALTER TABLE posts ENABLE ROW LEVEL SECURITY)
  5. Add policies before exposing to users
  6. Use @supabase/supabase-js in your frontend
  7. Use @supabase/ssr for Next.js/SvelteKit server-side auth

Managing a freelance or solo dev practice? Freelancer OS is a complete Notion workspace for client management, project tracking, and freelance finances — €19, one-time.

Top comments (0)