Overview
Supabase is an open-source Firebase alternative with built-in pgvector support. Combined with ModelRiver, you can store AI-generated embeddings alongside your regular application data, query them with semantic search, and generate answers: all inside one platform.
Quick start
Install dependencies
Bash
pip install supabase openaiEnable pgvector in Supabase
Run this in the Supabase SQL Editor:
SQL
1-- Enable the pgvector extension2create extension if not exists vector;3 4-- Create documents table with embedding column5create table documents (6 id bigserial primary key,7 content text not null,8 metadata jsonb default '{}',9 embedding vector(1536), -- Match your embedding model's dimensions10 created_at timestamptz default now()11);12 13-- Create an index for fast similarity search14create index on documents using ivfflat (embedding vector_cosine_ops) with (lists = 100);Setup clients
PYTHON
1from supabase import create_client2from openai import OpenAI3 4# Supabase5supabase = create_client(6 "https://YOUR_PROJECT.supabase.co",7 "YOUR_SUPABASE_ANON_KEY",8)9 10# ModelRiver11ai = OpenAI(12 base_url="https://api.modelriver.com/v1",13 api_key="mr_live_YOUR_API_KEY",14)Store embeddings
PYTHON
1def store_document(content: str, metadata: dict = {}):2 """Embed content via ModelRiver and store in Supabase."""3 # Generate embedding4 response = ai.embeddings.create(5 model="my-embedding-workflow",6 input=[content],7 )8 embedding = response.data[0].embedding9 10 # Insert into Supabase11 supabase.table("documents").insert({12 "content": content,13 "metadata": metadata,14 "embedding": embedding,15 }).execute()16 17# Store documents18store_document("ModelRiver routes AI requests across providers.", {"source": "docs"})19store_document("Workflows configure provider and fallback settings.", {"source": "docs"})20store_document("Structured outputs guarantee JSON compliance.", {"source": "docs"})Semantic search + RAG
Create a Supabase RPC function for similarity search:
SQL
1-- Supabase SQL Editor2create or replace function match_documents(3 query_embedding vector(1536),4 match_count int default 5,5 match_threshold float default 0.76)7returns table (id bigint, content text, metadata jsonb, similarity float)8language plpgsql9as $$10begin11 return query12 select13 documents.id,14 documents.content,15 documents.metadata,16 1 - (documents.embedding <=> query_embedding) as similarity17 from documents18 where 1 - (documents.embedding <=> query_embedding) > match_threshold19 order by documents.embedding <=> query_embedding20 limit match_count;21end;22$$;Python RAG function
PYTHON
1def ask(question: str, top_k: int = 3) -> str:2 """Semantic search + AI answer generation."""3 # Embed the question4 query_embedding = ai.embeddings.create(5 model="my-embedding-workflow",6 input=[question],7 ).data[0].embedding8 9 # Search Supabase10 results = supabase.rpc("match_documents", {11 "query_embedding": query_embedding,12 "match_count": top_k,13 }).execute()14 15 # Build context16 context = "\n\n".join([r["content"] for r in results.data])17 18 # Generate answer19 response = ai.chat.completions.create(20 model="my-chat-workflow",21 messages=[22 {"role": "system", "content": f"Answer based on context:\n\n{context}"},23 {"role": "user", "content": question},24 ],25 )26 27 return response.choices[0].message.content28 29print(ask("How does ModelRiver handle failover?"))JavaScript / Next.js
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 ai = new OpenAI({10 baseURL: "https://api.modelriver.com/v1",11 apiKey: process.env.MODELRIVER_API_KEY!,12});13 14async function ask(question: string): Promise<string> {15 const { data: embeddingData } = await ai.embeddings.create({16 model: "my-embedding-workflow",17 input: [question],18 });19 20 const { data: docs } = await supabase.rpc("match_documents", {21 query_embedding: embeddingData[0].embedding,22 match_count: 3,23 });24 25 const context = docs.map((d: any) => d.content).join("\n\n");26 27 const response = await ai.chat.completions.create({28 model: "my-chat-workflow",29 messages: [30 { role: "system", content: `Answer based on context:\n\n${context}` },31 { role: "user", content: question },32 ],33 });34 35 return response.choices[0].message.content!;36}Best practices
- Use IVFFlat or HNSW indexes for large datasets (>10k rows)
- Match embedding dimensions: Ensure your table's vector size matches your model
- Store original text: Keep content alongside embeddings for retrieval
- Use RLS policies: Supabase Row Level Security works with the documents table
- Monitor embedding costs in Request Logs
Next steps
- Pinecone integration: Managed vector DB alternative
- Next.js integration: Full-stack frontend
- API reference: Endpoint documentation