SupabaseAIVector SearchTutorial
Building AI Apps with Supabase: Vector Search and Beyond
How to use Supabase as your vector database for AI applications, with pgvector, embeddings, and semantic search.
3 min read
The Supabase + AI Stack
Supabase has quietly become a strong platform for AI applications. With native pgvector support, you get a production-ready vector database alongside your relational data. No extra infrastructure.
Why Supabase for AI?
- pgvector built in - no separate vector DB needed
- Row Level Security - secure your embeddings like any other data
- Edge Functions - run inference at the edge
- Realtime - stream AI responses to connected clients
Setting Up pgvector
Enable the vector extension in your Supabase project:
-- Enable the vector extension
create extension if not exists vector;
-- Create a documents table with embeddings
create table documents (
id bigserial primary key,
content text not null,
embedding vector(1536),
metadata jsonb default '{}'::jsonb,
created_at timestamptz default now()
);
-- Create an index for fast similarity search
create index on documents
using ivfflat (embedding vector_cosine_ops)
with (lists = 100);Generating Embeddings
Use Azure OpenAI to generate embeddings for your content:
import { AzureOpenAI } from "openai";
import { createClient } from "@supabase/supabase-js";
const openai = new AzureOpenAI({
endpoint: process.env.AZURE_OPENAI_ENDPOINT!,
apiKey: process.env.AZURE_OPENAI_KEY!,
apiVersion: "2024-10-21",
});
const supabase = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_KEY!
);
async function embedAndStore(content: string) {
// Generate embedding
const response = await openai.embeddings.create({
model: "text-embedding-3-small",
input: content,
});
const embedding = response.data[0].embedding;
// Store in Supabase
const { error } = await supabase.from("documents").insert({
content,
embedding,
});
if (error) throw error;
}Semantic Search
Now query your documents using cosine similarity:
async function search(query: string, limit = 5) {
// Embed the query
const response = await openai.embeddings.create({
model: "text-embedding-3-small",
input: query,
});
const embedding = response.data[0].embedding;
// Search by similarity
const { data, error } = await supabase.rpc("match_documents", {
query_embedding: embedding,
match_threshold: 0.78,
match_count: limit,
});
if (error) throw error;
return data;
}Create the matching function in SQL:
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
documents.id,
documents.content,
1 - (documents.embedding <=> query_embedding) as similarity
from documents
where 1 - (documents.embedding <=> query_embedding) > match_threshold
order by documents.embedding <=> query_embedding
limit match_count;
$$;Production Tips
- Use
text-embedding-3-small- It's 5x cheaper thanada-002with better performance - Batch your embeddings - The API supports up to 2048 inputs per request
- Index wisely - IVFFlat is great for < 1M vectors; switch to HNSW for larger datasets
- Cache embeddings - Store query embeddings to avoid re-computing for repeated searches
What's Next
In the next post, we'll combine this vector search with Azure OpenAI to build a full RAG (Retrieval-Augmented Generation) pipeline - complete with streaming responses and a polished UI.