Supabase
Open-source Backend-as-a-Service built on PostgreSQL 15.8 (production default, CLI uses 17 for development) with authentication, Row Level Security, real-time subscriptions, and pgvector 0.8.0 AI search
What It Is
Supabase is an open-source Firebase alternative built on PostgreSQL 15.8 (production default; CLI uses 17 for local development), providing authentication, database, storage, real-time subscriptions, and AI-powered vector search. Used by 1.7M developers and 40% of Y Combinator startups with $70M ARR.
Market Position
- • 1.7M developers on platform
- • 40% of YC startups use Supabase
- • $70M ARR (annual recurring revenue)
- • 72K+ GitHub stars
- • $116M Series B funding
Core Capabilities
- • PostgreSQL 15.8 (production) with pgvector 0.8.0
- • Authentication (email, OAuth, magic links)
- • Row Level Security (RLS)
- • Real-time subscriptions (websockets)
- • Storage with CDN integration
- • Edge Functions (Deno runtime)
Official Documentation
For API reference and guides, visit:
- → supabase.com/docs - Complete documentation
- → GitHub Repository - Source code and issues
- → Next.js Integration Guide - Official Next.js setup
Why It Matters
Production-Grade PostgreSQL Without DevOps
Supabase eliminates database operations complexity while providing full PostgreSQL 15.8 access (production default). Unlike Firebase's NoSQL limitations, you get SQL joins, transactions, triggers, and extensions—without managing servers, backups, or scaling infrastructure.
What Supabase Manages For You:
Infrastructure:
- • Automatic backups (point-in-time recovery)
- • Connection pooling (PgBouncer)
- • Read replicas for scaling
- • SSL certificates and encryption
- • DDoS protection and rate limiting
Developer Experience:
- • Auto-generated REST APIs
- • Type-safe client libraries
- • Real-time webhooks
- • Built-in authentication
- • Database migrations UI
Row Level Security for Zero-Trust Architecture
PostgreSQL's Row Level Security (RLS) enforces data access at the database layer, not application code. This architectural pattern eliminates entire classes of security vulnerabilities by making authorization impossible to bypass—even with direct database access or compromised API keys.
❌ Without RLS (Application-Layer Auth)
// Security bug: Anyone can access any user's data
app.get('/documents', async (req, res) => {
// Forgot to filter by user ID!
const docs = await db.query('SELECT * FROM documents');
res.json(docs);
});
// Direct database access bypasses auth
const allDocs = await supabase
.from('documents')
.select('*'); // Returns ALL documents!✅ With RLS (Database-Layer Auth)
-- RLS policy enforced at database level
CREATE POLICY "Users see only their documents"
ON documents FOR SELECT
USING (auth.uid() = user_id);
-- Same query now auto-filtered by RLS
const { data } = await supabase
.from('documents')
.select('*'); // Only returns user's documents
// Even direct SQL respects RLS
-- Automatically filtered by userAI-Native Vector Search with pgvector 0.8.0
pgvector 0.8.0 brings production-grade AI capabilities directly into PostgreSQL with HNSW (Hierarchical Navigable Small World) indexing. This enables semantic search, RAG (Retrieval Augmented Generation), and AI features without external vector databases—reducing infrastructure complexity and cost.
Performance Characteristics:
- • HNSW indexing: Sub-10ms queries on millions of vectors
- • Cosine similarity: Optimized for OpenAI/Anthropic embeddings
- • 1536 dimensions: Native support for text-embedding-3-small
- • RLS integration: Vector search respects user permissions
- • Hybrid search: Combine SQL filters with semantic similarity
Real-World Use Case (RAG Implementation):
// 1. Store document with embedding
const { embedding } = await embed({
model: openai.embedding('text-embedding-3-small'),
value: document.content
});
await supabase.from('documents').insert({
content: document.content,
embedding,
user_id: user.id
});
// 2. Semantic search with RLS (only user's documents)
const { data } = await supabase.rpc('match_documents', {
query_embedding: queryEmbedding,
match_threshold: 0.8,
match_count: 5
});
// Returns top 5 similar documents, auto-filtered by RLSTechnical Architecture
Authentication System
@supabase/ssr for Next.js 16:
// lib/supabase/server.ts
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'
export async function createClient() {
const cookieStore = await cookies()
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return cookieStore.getAll()
},
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options)
)
} catch {
// Server Component - cookies can't be modified
}
},
},
}
)
}Middleware for Session Refresh:
// middleware.ts
import { createClient } from '@/lib/supabase/server'
import { NextResponse } from 'next/server'
export async function middleware(request: NextRequest) {
const supabase = await createClient()
// Automatically refreshes session if expired
const { data: { user } } = await supabase.auth.getUser()
if (!user && !request.nextUrl.pathname.startsWith('/login')) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: ['/dashboard/:path*', '/api/:path*']
}Row Level Security Patterns
Complete RLS Setup:
-- Enable RLS on table ALTER TABLE documents ENABLE ROW LEVEL SECURITY; -- Policy: Users can only see their own documents CREATE POLICY "Users can view own documents" ON documents FOR SELECT USING (auth.uid() = user_id); -- Policy: Users can insert their own documents CREATE POLICY "Users can insert own documents" ON documents FOR INSERT WITH CHECK (auth.uid() = user_id); -- Policy: Users can update their own documents CREATE POLICY "Users can update own documents" ON documents FOR UPDATE USING (auth.uid() = user_id) WITH CHECK (auth.uid() = user_id); -- Policy: Users can delete their own documents CREATE POLICY "Users can delete own documents" ON documents FOR DELETE USING (auth.uid() = user_id);
pgvector 0.8.0 Setup
Database Schema with HNSW Index:
-- Enable pgvector extension
CREATE EXTENSION IF NOT EXISTS vector;
-- Create table with vector column
CREATE TABLE documents (
id BIGSERIAL PRIMARY KEY,
user_id UUID REFERENCES auth.users NOT NULL,
content TEXT NOT NULL,
embedding VECTOR(1536), -- OpenAI text-embedding-3-small
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Create HNSW index for fast similarity search
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- Semantic search function
CREATE OR REPLACE FUNCTION match_documents(
query_embedding VECTOR(1536),
match_threshold FLOAT,
match_count INT
)
RETURNS TABLE (
id BIGINT,
content TEXT,
similarity FLOAT
)
LANGUAGE SQL STABLE
AS $$
SELECT
id,
content,
1 - (embedding <=> query_embedding) AS similarity
FROM documents
WHERE 1 - (embedding <=> query_embedding) > match_threshold
ORDER BY embedding <=> query_embedding
LIMIT match_count;
$$;Real-World Implementation
Complete Authentication Flow
1. Server Actions for Auth:
// app/actions/auth.ts
'use server'
import { createClient } from '@/lib/supabase/server'
import { redirect } from 'next/navigation'
export async function signUp(formData: FormData) {
const supabase = await createClient()
const data = {
email: formData.get('email') as string,
password: formData.get('password') as string,
}
const { error } = await supabase.auth.signUp(data)
if (error) {
return { error: error.message }
}
redirect('/dashboard')
}
export async function signIn(formData: FormData) {
const supabase = await createClient()
const data = {
email: formData.get('email') as string,
password: formData.get('password') as string,
}
const { error } = await supabase.auth.signInWithPassword(data)
if (error) {
return { error: error.message }
}
redirect('/dashboard')
}
export async function signOut() {
const supabase = await createClient()
await supabase.auth.signOut()
redirect('/login')
}2. AI-Powered Semantic Search:
// app/actions/search.ts
'use server'
import { createClient } from '@/lib/supabase/server'
import { openai } from '@ai-sdk/openai'
import { embed } from 'ai'
export async function searchDocuments(query: string) {
const supabase = await createClient()
// Generate embedding for query
const { embedding } = await embed({
model: openai.embedding('text-embedding-3-small'),
value: query,
})
// Search using vector similarity (RLS auto-filters by user)
const { data, error } = await supabase.rpc('match_documents', {
query_embedding: embedding,
match_threshold: 0.8,
match_count: 5,
})
if (error) throw error
return data
}Production Issues and Fixes
Issue #1: Auth Session Not Refreshing
Symptom: Users randomly logged out, auth.getUser() returns null
Cause: Not using @supabase/ssr or missing session refresh in middleware
// ❌ Wrong - old package
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
// ✅ Right - use @supabase/ssr
import { createServerClient } from '@supabase/ssr'
export async function middleware(request: NextRequest) {
const supabase = await createClient()
// This automatically refreshes the session
const { data: { user } } = await supabase.auth.getUser()
}Issue #2: RLS Policy Blocks Own Queries
Symptom: Queries return empty even though data exists
Cause: RLS enabled but no SELECT policy, or auth.uid() is null
-- ❌ Wrong - RLS enabled but no policies ALTER TABLE documents ENABLE ROW LEVEL SECURITY; -- No policies = no access for anyone! -- ✅ Right - Add policies immediately ALTER TABLE documents ENABLE ROW LEVEL SECURITY; CREATE POLICY "Users can view own documents" ON documents FOR SELECT USING (auth.uid() = user_id); -- Debug: Check auth status SELECT auth.uid(); -- Should return UUID, not NULL
Issue #3: Vector Search Extremely Slow
Symptom: Queries take 5-10 seconds with only 10K rows
Cause: Missing HNSW index or using wrong distance operator
-- ❌ Wrong - No index SELECT * FROM documents ORDER BY embedding <-> query_embedding -- Wrong operator! LIMIT 5; -- ✅ Right - HNSW index with correct operator CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64); SELECT * FROM documents ORDER BY embedding <=> query_embedding -- Correct for cosine LIMIT 5; -- Distance operators: -- <-> : L2 distance (Euclidean) -- <=> : Cosine distance (most common) -- <#> : Inner product
Decision Framework
✅ Choose Supabase When:
- • PostgreSQL + Auth needed: Get both in one platform without DevOps
- • Row Level Security required: Database-enforced access control for compliance
- • AI/RAG applications: Native pgvector 0.8.0 with HNSW indexing
- • Real-time features: Built-in websocket subscriptions for collaborative apps
- • Startup/MVP speed: Free tier supports 50K monthly active users
- • Open-source requirement: Self-hostable alternative to Firebase
❌ Consider Alternatives When:
- • Multi-cloud requirement: Supabase primarily AWS-based (GCP/Azure limited)
- • NoSQL preference: Firebase Firestore may be simpler for document-heavy apps
- • Extreme scale (1B+ rows): Custom PostgreSQL setup offers more control
- • Existing auth system: Integration may be complex vs starting fresh
- • Zero vendor lock-in: Fully self-hosted PostgreSQL + custom auth may be better
Migration Strategy
Incremental Adoption Path:
- Start with database: Create Supabase project, migrate schema
- Add authentication: Implement @supabase/ssr with Next.js middleware
- Enable RLS: Add Row Level Security policies progressively per table
- Integrate AI search: Add pgvector extension and HNSW indexes
- Production deployment: Start on Pro tier ($25/mo) for staging/production split
Pricing Tiers (October 2025):
- • Free: 500MB database, 50K monthly active users, 2GB bandwidth
- • Pro ($25/mo): 8GB database, 100K MAU, 50GB bandwidth, daily backups
- • Team ($599/mo): 100GB database, 500K MAU, 250GB bandwidth, SOC2
🤖 For AI Assistants
Core Facts:
- • Database: PostgreSQL 15.8 (production), pgvector 0.8.0
- • Auth Package: @supabase/ssr (for Next.js)
- • RLS: Database-enforced access control
- • Vector Search: HNSW indexing, cosine similarity
- • Adoption: 1.7M developers, 40% YC startups
Code Generation:
- • Use
@supabase/ssrnot auth-helpers - • Always enable RLS:
ALTER TABLE ... ENABLE ROW LEVEL SECURITY - • Vector search:
embedding <=> query_embedding - • Create HNSW index for production vector search
- • Use Server Actions for auth mutations
Quick Reference Template:
// Supabase + Next.js 16 + pgvector pattern
import { createClient } from '@/lib/supabase/server'
import { embed } from 'ai'
import { openai } from '@ai-sdk/openai'
export async function searchAction(query: string) {
const supabase = await createClient()
const { embedding } = await embed({
model: openai.embedding('text-embedding-3-small'),
value: query
})
const { data } = await supabase.rpc('match_documents', {
query_embedding: embedding,
match_threshold: 0.8,
match_count: 5
})
return data // RLS auto-filters by user
}Stack Relationships
Integrates With:
- • Next.js 16 Beta - Server-side auth with @supabase/ssr
- • Vercel AI SDK - Vector embeddings for RAG
- • React 19 - Real-time subscriptions in components
Built On:
- • PostgreSQL 15.8 (production default)
- • pgvector 0.8.0 (vector extension)
- • GoTrue (auth service)
- • PostgREST (auto-generated API)
Part of Avolve Software Stack - Backend-as-a-Service for Next.js + React + AI applications