When building AI applications, one of the most critical components is the data layer. Where will you store your embeddings? How will you do semantic search? How will you combine traditional keyword search with vector search? Supabase answers all these questions from a single platform, and in 2025 it reached a $5 billion valuation doing exactly that. Built on top of PostgreSQL, this open-source platform offers an AI-native infrastructure through its pgvector integration. Let's take a deep dive into Supabase's AI capabilities, its hybrid search architecture, and how to use it in production.
💡 Note: All code examples in this article were tested with the Supabase JS SDK v2.x and pgvector 0.7+. For the current API reference, visit the Supabase Docs. You can explore the open-source repo on GitHub.
Table of Contents
- What Is Supabase and Why $5B?
- The Valuation Journey
- Core Architecture
- Vector Search with pgvector
- Installing the Extension
- Creating and Storing Embeddings with TypeScript
- RPC Function (SQL)
- Index Strategies
- Hybrid Search: BM25 + Vector
- Hybrid Search Function
- Why Hybrid?
- Vector Buckets and Large Scale
- Bucket Architecture
- postgres.new: PostgreSQL in the Browser
- Key Features
- Building a RAG Pipeline
- RAG API with an Edge Function
- Firebase vs. Supabase Comparison
- Quick Start: the match_documents RPC
- Production Best Practices
- 1. Connection Pooling
- 2. Embedding Cache
- 3. Row Level Security (RLS)
- Conclusion and Recommendations
- Recommendations
What Is Supabase and Why $5B?
Supabase set out in 2020 under the slogan "the open-source Firebase alternative." But by 2025 it had become much more than that. Being able to use the power of PostgreSQL directly, security via Row Level Security (RLS), real-time subscriptions, Edge Functions, and most importantly AI-native vector search with pgvector — all of this on a single platform.
The Valuation Journey
Year | Valuation | Key Milestone |
|---|---|---|
2020 | $6M (Seed) | First launch, "Open Source Firebase" |
2021 | $116M (Series A) | Auth, Storage, Edge Functions |
2022 | $500M (Series B) | pgvector integration, Realtime v2 |
2023 | $1B (Series C) | Vector columns, AI toolkit |
2024 | $2B | Branching, postgres.new |
2025 | $5B (Series D) | Hybrid Search, Vector Buckets, Enterprise |
This growth isn't a coincidence. The AI wave exploded demand for PostgreSQL-based solutions. Supabase caught this wave perfectly with its "keep everything in Postgres" philosophy. Follow the Supabase Blog for the detailed roadmap.
Core Architecture
Behind Supabase are fully open-source components:
- PostgreSQL — The main database (including the pgvector and pg_bm25 extensions)
- PostgREST — Automatic REST API
- GoTrue — Authentication
- Realtime — WebSocket-based real-time subscriptions
- Storage — S3-compatible file storage
- Edge Functions — Deno-based serverless functions
- pg_graphql — Automatic GraphQL API
🔍 Pro Tip: Don't think of Supabase as just a Firebase alternative. The combination of pgvector + RLS + Edge Functions makes it an ideal backend for AI applications. In our Firebase Advanced (in Turkish) article we also covered Firebase's strengths.
Vector Search with pgvector
pgvector is an extension that adds a vector data type and similarity search capabilities to PostgreSQL. Supabase offers this out of the box.
Installing the Extension
1-- From the Supabase Dashboard or SQL Editor2CREATE EXTENSION IF NOT EXISTS vector;3 4-- Create a table with a vector column5CREATE TABLE documents (6 id BIGSERIAL PRIMARY KEY,7 content TEXT NOT NULL,8 embedding VECTOR(1536), -- OpenAI text-embedding-3-small dimension9 metadata JSONB DEFAULT '{}',10 created_at TIMESTAMPTZ DEFAULT NOW()11);12 13-- HNSW index for performance14CREATE INDEX ON documents15 USING hnsw (embedding vector_cosine_ops)16 WITH (m = 16, ef_construction = 200);Creating and Storing Embeddings with TypeScript
1import { createClient } from '@supabase/supabase-js';2import OpenAI from 'openai';3 4const supabase = createClient(5 process.env.SUPABASE_URL!,6 process.env.SUPABASE_ANON_KEY!7);8 9const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });10 11interface Document {12 id: number;13 content: string;14 embedding: number[];15 metadata: Record<string, unknown>;16 similarity?: number;17}18 19// Create and store the embedding20async function embedAndStore(content: string, metadata: Record<string, unknown>) {21 const embeddingResponse = await openai.embeddings.create({22 model: 'text-embedding-3-small',23 input: content,24 });25 26 const embedding = embeddingResponse.data[0].embedding;27 28 const { data, error } = await supabase29 .from('documents')30 .insert({31 content,32 embedding,33 metadata,34 })35 .select()36 .single();37 38 if (error) throw new Error(`Save error: ${error.message}`);39 return data as Document;40}41 42// Semantic search43async function semanticSearch(query: string, limit = 10): Promise<Document[]> {44 const embeddingResponse = await openai.embeddings.create({45 model: 'text-embedding-3-small',46 input: query,47 });48 49 const queryEmbedding = embeddingResponse.data[0].embedding;50 51 const { data, error } = await supabase.rpc('match_documents', {52 query_embedding: queryEmbedding,53 match_threshold: 0.7,54 match_count: limit,55 });56 57 if (error) throw new Error(`Search error: ${error.message}`);58 return data as Document[];59}RPC Function (SQL)
1CREATE OR REPLACE FUNCTION match_documents(2 query_embedding VECTOR(1536),3 match_threshold FLOAT DEFAULT 0.7,4 match_count INT DEFAULT 105)6RETURNS TABLE (7 id BIGINT,8 content TEXT,9 metadata JSONB,10 similarity FLOAT11)12LANGUAGE plpgsql13AS $$14BEGIN15 RETURN QUERY16 SELECT17 d.id,18 d.content,19 d.metadata,20 1 - (d.embedding <=> query_embedding) AS similarity21 FROM documents d22 WHERE 1 - (d.embedding <=> query_embedding) > match_threshold23 ORDER BY d.embedding <=> query_embedding24 LIMIT match_count;25END;26$$;Index Strategies
pgvector offers two main index types:
Index Type | Speed | Accuracy | Memory | When to Use? |
|---|---|---|---|---|
IVFFlat | Fast build | ~95% recall | Low | < 1M vectors, frequent updates |
HNSW | Slow build | ~99% recall | High | > 100K vectors, read-heavy |
🔍 Pro Tip: If you have fewer than 100,000 vectors, IVFFlat is enough. But if you'll index millions of documents in production, choose HNSW. Keep the ef_search parameter between 100-200 — that's the ideal balance between accuracy and speed.
Hybrid Search: BM25 + Vector
Pure vector search is great but not enough. When a user asks "Was middleware removed in Next.js 15?", semantic similarity alone doesn't guarantee the right result. This is where hybrid search comes in.
In 2025 Supabase started offering hybrid search that combines keyword-based BM25 scoring via the pg_bm25 extension with vector similarity.
Hybrid Search Function
1-- BM25 extension2CREATE EXTENSION IF NOT EXISTS pg_bm25;3 4-- Full-text search index5CREATE INDEX idx_documents_fts ON documents6 USING bm25 (content)7 WITH (text_fields = '{"content": {}}');8 9-- Hybrid search: combining BM25 + Vector10CREATE OR REPLACE FUNCTION hybrid_search(11 query_text TEXT,12 query_embedding VECTOR(1536),13 bm25_weight FLOAT DEFAULT 0.3,14 vector_weight FLOAT DEFAULT 0.7,15 match_count INT DEFAULT 1016)17RETURNS TABLE (18 id BIGINT,19 content TEXT,20 metadata JSONB,21 bm25_score FLOAT,22 vector_score FLOAT,23 combined_score FLOAT24)25LANGUAGE plpgsql26AS $$27BEGIN28 RETURN QUERY29 WITH bm25_results AS (30 SELECT d.id, d.content, d.metadata,31 paradedb.score(d.id) AS score32 FROM documents d33 WHERE d.content @@@ query_text34 ORDER BY score DESC35 LIMIT match_count * 336 ),37 vector_results AS (38 SELECT d.id, d.content, d.metadata,39 1 - (d.embedding <=> query_embedding) AS score40 FROM documents d41 ORDER BY d.embedding <=> query_embedding42 LIMIT match_count * 343 ),44 combined AS (45 SELECT46 COALESCE(b.id, v.id) AS id,47 COALESCE(b.content, v.content) AS content,48 COALESCE(b.metadata, v.metadata) AS metadata,49 COALESCE(b.score, 0) AS bm25_score,50 COALESCE(v.score, 0) AS vector_score,51 (COALESCE(b.score, 0) * bm25_weight +52 COALESCE(v.score, 0) * vector_weight) AS combined_score53 FROM bm25_results b54 FULL OUTER JOIN vector_results v ON b.id = v.id55 )56 SELECT c.id, c.content, c.metadata,57 c.bm25_score, c.vector_score, c.combined_score58 FROM combined c59 ORDER BY c.combined_score DESC60 LIMIT match_count;61END;62$$;Why Hybrid?
- Keyword search (BM25): Exact matches, technical terms, proper nouns
- Vector search: Semantic similarity, paraphrasing, the same concept across different languages
- Hybrid: Combines the strengths of both
Especially in technical documentation, e-commerce product search, and customer support chatbots, hybrid search delivers 15-25% higher recall.
🔍 Pro Tip: Adjust the bm25_weight and vector_weight ratios based on your use case. Raise BM25 to 0.4-0.5 for technical documentation, and raise vector to 0.8 for general chatbot conversations.
Vector Buckets and Large Scale
Once you have 10 million+ embeddings, searching a single table gets slow. Supabase's Vector Buckets feature narrows the search space by splitting vectors into logical groups.
Bucket Architecture
1// Namespace-based vector grouping2interface VectorBucket {3 namespace: string; // 'blog', 'docs', 'support'4 partition: string; // 'tr', 'en', 'de'5}6 7async function searchInBucket(8 query: string,9 bucket: VectorBucket,10 limit = 1011) {12 const embedding = await generateEmbedding(query);13 14 const { data } = await supabase.rpc('match_documents_in_bucket', {15 query_embedding: embedding,16 target_namespace: bucket.namespace,17 target_partition: bucket.partition,18 match_count: limit,19 });20 21 return data;22}23 24// Usage25const blogResults = await searchInBucket(26 'How do React Server Components work?',27 { namespace: 'blog', partition: 'tr' }28);Even with 50M vectors, this approach means every search only scans the relevant bucket's vectors. Result: 10x-100x speedup.
postgres.new: PostgreSQL in the Browser
One of Supabase's wildest projects: a full-fledged PostgreSQL that runs in the browser. Thanks to PGlite, built on WebAssembly, you can experiment with pgvector without setting up any server.
Key Features
- PostgreSQL running in the browser (WebAssembly)
- pgvector extension support
- Instant data loading via CSV/JSON import
- Writing SQL in natural language with an AI assistant
- Visualizing results as charts
- Deploying your project directly to Supabase
This is fantastic for prototyping and learning. If you want to try out a RAG pipeline, you can get started on postgres.new in 5 minutes. In our MCP Protocol (in Turkish) article we mentioned AI tool integration, and there's also a Supabase MCP server available for that.
Building a RAG Pipeline
Retrieval Augmented Generation (RAG) is the most effective way to reduce hallucination in large language models. Here's how to set up a production-ready RAG pipeline with Supabase:
RAG API with an Edge Function
1// supabase/functions/rag-chat/index.ts2import { serve } from 'https://deno.land/[email protected]/http/server.ts';3import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';4import OpenAI from 'https://esm.sh/openai@4';5 6const supabase = createClient(7 Deno.env.get('SUPABASE_URL')!,8 Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!9);10 11const openai = new OpenAI({12 apiKey: Deno.env.get('OPENAI_API_KEY')!,13});14 15serve(async (req: Request) => {16 const { query, history = [] } = await req.json();17 18 // 1. Create the query embedding19 const embeddingRes = await openai.embeddings.create({20 model: 'text-embedding-3-small',21 input: query,22 });23 24 // 2. Find relevant documents with hybrid search25 const { data: documents } = await supabase.rpc('hybrid_search', {26 query_text: query,27 query_embedding: embeddingRes.data[0].embedding,28 bm25_weight: 0.3,29 vector_weight: 0.7,30 match_count: 5,31 });32 33 // 3. Build the context34 const context = documents35 ?.map((d: { content: string }) => d.content)36 .join('\n\n---\n\n');37 38 // 4. Generate the answer with the LLM39 const completion = await openai.chat.completions.create({40 model: 'gpt-4o',41 messages: [42 {43 role: 'system',44 content: `You are a helpful assistant. Answer questions based on the context below.45If the context doesn't contain the answer, say "I don't have information about this."46 47Context:48${context}`,49 },50 ...history,51 { role: 'user', content: query },52 ],53 temperature: 0.2,54 max_tokens: 1000,55 });56 57 return new Response(58 JSON.stringify({59 answer: completion.choices[0].message.content,60 sources: documents?.map((d: { id: number; metadata: Record<string, unknown> }) => ({61 id: d.id,62 metadata: d.metadata,63 })),64 }),65 { headers: { 'Content-Type': 'application/json' } }66 );67});Firebase vs. Supabase Comparison
Let's compare the two, as someone who has used both in production. In our Firebase Advanced (in Turkish) article we went deep on Firebase's details. Now let's compare them with an AI focus:
Feature | Supabase | Firebase |
|---|---|---|
Database | PostgreSQL (SQL) | Firestore (NoSQL) |
Vector Search | pgvector (native) | Firestore Vector Search (limited) |
Hybrid Search | BM25 + Vector | None (requires external service) |
Real-time | WebSocket | WebSocket |
Auth | GoTrue (OAuth, Magic Link) | Firebase Auth (very extensive) |
Functions | Deno Edge Functions | Cloud Functions (Node.js) |
AI Integration | pgvector + Edge Functions | Vertex AI, Gemini API |
Open Source | Yes, fully | No |
Self-host | Easy with Docker | Firebase Emulator (limited) |
Pricing | Predictable | Usage-based (surprising) |
🔍 Pro Tip: You can also use both together! Firebase Auth + Supabase Database, or Firebase Hosting + Supabase Vector Search. Pick tools pragmatically, not dogmatically. The repository pattern in our Flutter Clean Architecture article makes this kind of integration easier.
Quick Start: the match_documents RPC
The pattern most commonly used in production applications — doing a vector search with a single RPC call:
1// Supabase Vector Search — the most common usage2const { data } = await supabase.rpc('match_documents', {3 query_embedding: embedding,4 match_threshold: 0.78,5 match_count: 106});We defined the match_documents SQL function needed for this call in the RPC Function section above. The value match_threshold: 0.78 is the optimal starting point for most use cases — lowering it gets you more but less relevant results, raising it gets you fewer but higher-quality results.
Production Best Practices
1. Connection Pooling
1// Supabase provides automatic connection pooling (PgBouncer)2// But in Edge Functions every request opens a new connection3// Solution: define the Supabase client in global scope4const supabase = createClient(url, key, {5 db: { schema: 'public' },6 auth: { persistSession: false },7 global: {8 headers: { 'x-connection-pool': 'true' },9 },10});2. Embedding Cache
Making an OpenAI API call on every query is both slow and expensive. Cache frequently asked queries:
1// A cache table in Redis or Supabase2const CACHE_TTL = 3600; // 1 hour3 4async function getCachedEmbedding(query: string): Promise<number[] | null> {5 const cacheKey = createHash('sha256').update(query).digest('hex');6 7 const { data } = await supabase8 .from('embedding_cache')9 .select('embedding')10 .eq('cache_key', cacheKey)11 .gt('expires_at', new Date().toISOString())12 .single();13 14 return data?.embedding ?? null;15}3. Row Level Security (RLS)
1-- Users should only see their own documents2ALTER TABLE documents ENABLE ROW LEVEL SECURITY;3 4CREATE POLICY "Users see own documents"5 ON documents FOR SELECT6 USING (auth.uid()::text = metadata->>'user_id');7 8CREATE POLICY "Users insert own documents"9 ON documents FOR INSERT10 WITH CHECK (auth.uid()::text = metadata->>'user_id');Conclusion and Recommendations
Supabase is one of the strongest AI-native backend options for 2025-2026. Native vector search with pgvector, keyword+semantic combination via hybrid search, scalability with Vector Buckets, and instant prototyping with postgres.new — all within an open-source ecosystem. Consider Supabase as a backend for your GraphQL Mobile or WebSocket Real-Time (in Turkish) projects.
Recommendations
- Starting a new AI project? Begin with Supabase + pgvector
- Already have a Firebase project? Use Supabase as an additional service for vector search
- Enterprise scale? Set up the Vector Buckets + HNSW index + connection pooling combo
- Just getting started learning? Try it for free on postgres.new
GOLDEN TIP
The most valuable insight in this article
This tip holds the article's most important takeaway.
Easter Egg
You found a hidden gem!
There's a hidden detail in this section. Want to uncover it?
Reader Reward
A production-ready chatbot in 30 minutes with Supabase + Vercel AI SDK: the useChat hook from Vercel's ai package + a Supabase Edge Function RAG endpoint = streaming chat with zero state management on the frontend. With experimental_StreamData you can also show source documents during the stream. This combo is the fastest path to a full AI chatbot MVP.
Tags
iOS Development News
Weekly Swift tips, SwiftUI tricks and iOS best practices. No spam, only valuable content.
We respect your privacy. You can unsubscribe at any time.

