Claude + Supabase: Real-Time RAG for Dynamic Web Apps
Supercharge web apps with real-time RAG using Claude and Supabase. Serverless vector search with pgvector delivers dynamic knowledge retrieval—no external stores required.
Unlock Real-Time RAG: Why Claude + Supabase?
Retrieval-Augmented Generation (RAG) transforms Claude AI from a static responder into a dynamic knowledge powerhouse. Pairing it with Supabase's realtime Postgres database and pgvector extension enables edge-deployed, serverless RAG for web apps that update instantly as data evolves.
Key Benefits:
- Realtime Updates: Supabase subscriptions push document changes, keeping your vector index fresh without polling.
- Serverless Scale: Edge Functions run RAG pipelines close to data, minimizing latency for global users.
- No External Dependencies: pgvector handles embeddings natively; Claude SDK powers generation.
- Edge AI Efficiency: Deno-based functions with transformers.js for lightweight query embedding.
- Claude-Optimized: Leverage Sonnet 3.5 for superior context handling in dynamic scenarios.
Ideal for dashboards, customer support bots, or collaborative apps where knowledge bases live-update.
Prerequisites
Before diving in:
- Supabase account (free tier suffices).
- Anthropic API key for Claude SDK.
- Node.js/Deno familiarity (for local testing).
- Basic SQL knowledge for schema setup.
Tools & Packages:
- Supabase JS client (
@supabase/supabase-js). - Anthropic SDK (
@anthropic-ai/sdk). - Transformers.js (
@xenova/transformers) for client-side embeddings in Deno.
Step 1: Create Supabase Project and Enable pgvector
- Log into Supabase Dashboard > New Project.
- Name it (e.g.,
claude-rag-app), set region (closest to users for edge latency). - In SQL Editor, run:
-- Enable pgvector
create extension if not exists vector;
-- Documents table with vector column (1536 dims for common embedders)
create table documents (
id bigserial primary key,
content text not null,
metadata jsonb,
embedding vector(1536)
);
-- Index for fast similarity search
create index on documents using ivfflat (embedding vector_cosine_ops);
- Set
match_limitconfig:
alter table documents set (associated_indexes = array['documents_embedding_idx']);
Step 2: Ingest Sample Data with Embeddings
Use an Edge Function to embed and upsert documents. First, create ingest function.
In Dashboard > Edge Functions > New Function:
// deno-lint-ignore-file
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
import { Anthropic } from 'npm:@anthropic-ai/sdk@0.9.5';
import { pipeline, env } from 'npm:@xenova/transformers@2.17.2';
import { createClient } from 'npm:@supabase/supabase-js@2';
env.allowLocalModels = false;
// Secrets injected by Supabase
const supabaseUrl = Deno.env.get('SUPABASE_URL')!;
const supabaseKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!;
const anthropicKey = Deno.env.get('ANTHROPIC_API_KEY')!;
const supabase = createClient(supabaseUrl, supabaseKey);
const anthropic = new Anthropic({ apiKey: anthropicKey });
const embedder = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2');
function embedding(text: string): Promise<number[]> {
return embedder(text, { pooling: 'mean', normalize: true }).then((output: any) => Array.from(output.data));
}
serve(async (req) => {
const { content, metadata } = await req.json();
const emb = await embedding(content);
const { data, error } = await supabase.from('documents').upsert({
content, metadata, embedding: emb
});
if (error) return new Response(JSON.stringify({ error }), { status: 400 });
return new Response(JSON.stringify({ success: true, id: data[0].id }));
});
Deploy and test via curl:
curl -X POST https://your-project.supabase.co/functions/v1/ingest \
-H "Authorization: Bearer YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{"content": "Claude 3.5 Sonnet excels at RAG.", "metadata": {"source": "blog"}}'
Step 3: Build the RAG Query Edge Function
Create rag-query function for embedding queries, retrieving contexts, and calling Claude.
// deno-lint-ignore-file
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
import { Anthropic } from 'npm:@anthropic-ai/sdk@0.9.5';
import { pipeline, env } from 'npm:@xenova/transformers@2.17.2';
import { createClient } from 'npm:@supabase/supabase-js@2';
env.allowLocalModels = false;
const supabaseUrl = Deno.env.get('SUPABASE_URL')!;
const supabaseKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!;
const anthropicKey = Deno.env.get('ANTHROPIC_API_KEY')!;
const supabase = createClient(supabaseUrl, supabaseKey);
const anthropic = new Anthropic({ apiKey: anthropicKey });
const embedder = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2');
function getEmbedding(text: string): Promise<number[]> {
return embedder(text, { pooling: 'mean', normalize: true }).then((output: any) => Array.from(output.data));
}
serve(async (req) => {
const { query, maxResults = 3 } = await req.json();
const queryEmb = await getEmbedding(query);
// Vector search
const { data: contexts } = await supabase.rpc('match_documents', {
query_embedding: queryEmb,
match_threshold: 0.78,
match_count: maxResults
});
const context = contexts.map((c: any) => c.content).join('\
\
');
// Claude RAG prompt
const msg = await anthropic.messages.create({
model: 'claude-3-5-sonnet-20241022',
max_tokens: 1024,
messages: [{
role: 'user',
content: `Use this context to answer: ${context}\
\
Q: ${query}\
Answer concisely and accurately.`
}],
stream: false
});
return new Response(JSON.stringify({ answer: msg.content[0].text, contexts }));
});
Add RPC Function in SQL Editor:
create or replace function match_documents(
query_embedding vector(1536),
match_threshold float,
match_count int
)
returns table (id bigint, content text, metadata jsonb, similarity float)
language sql stable
as $$
select
documents.id,
documents.content,
documents.metadata,
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;
$$;
Step 4: Set Up Realtime Subscriptions
Realtime ensures your app reacts to DB changes instantly. In frontend, subscribe to documents.
Step 5: Frontend Integration Example
HTML/JS app with Supabase client:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
</head>
<body>
<input id="query" placeholder="Ask about Claude...">
<button onclick="ragQuery()">Query</button>
<div id="answer"></div>
<div id="docs"></div>
<script>
const supabase = Supabase.createClient('YOUR_URL', 'YOUR_ANON_KEY');
// Realtime subscription
supabase.channel('docs').on('postgres_changes',
{ event: '*', schema: 'public', table: 'documents' },
(payload) => {
console.log('Docs updated:', payload);
loadDocs();
}
).subscribe();
async function loadDocs() {
const { data } = await supabase.from('documents').select();
document.getElementById('docs').innerHTML = data.map(d => `<p>${d.content}</p>`).join('');
}
async function ragQuery() {
const query = document.getElementById('query').value;
const res = await fetch('https://your-project.supabase.co/functions/v1/rag-query', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query })
});
const { answer } = await res.json();
document.getElementById('answer').innerText = answer;
}
loadDocs();
</script>
</body>
</html>
Changes to documents (via ingest) trigger UI updates, and queries always use latest data.
Step 6: Deploy and Secure
- Add secrets in Edge Functions settings:
ANTHROPIC_API_KEY, etc. - Enable RLS on
documents:
alter table documents enable row level security;
create policy "Public read" on documents for select using (true);
- Use service role for functions; anon for client.
Step 7: Optimize for Production
Claude Prompt Engineering Tips:
- Chunking: Split long docs into 512-token chunks before embedding.
- Hybrid Search: Combine vector + full-text:
where similarity > thresh and content ilike '%keyword%'. - Streaming: Set
stream: truein Claude API for live responses. - Model Choice: Sonnet for complex RAG; Haiku for speed.
Performance Tweaks:
- HNSW index:
create index on documents using hnsw (embedding vector_cosine_ops); - Batch ingest for scale.
- Cache frequent queries with Supabase's
pgbouncer.
Cost Breakdown:
- Supabase: ~$0.125/GB storage + compute.
- Claude: $3/M input tokens (Sonnet).
- Embeddings: Free on-edge with transformers.js.
Advanced: Streaming Realtime RAG
Enhance rag-query for streaming:
// In messages.create, set stream: true
anthropic.messages.stream({...}).then(stream => {
// Pipe to client via ReadableStream
});
Use new ReadableStream({ async start(controller) { ... } }) for server-sent events.
Troubleshooting Common Issues
- Embedding Dim Mismatch: Ensure 1536 for MiniLM.
- Cold Starts: Edge Functions warm-up with cron pings.
- Rate Limits: Implement retry with exponential backoff.
- CORS: Auto-handled by Supabase.
Test Query: Ingest Claude docs, query "Best Claude model for coding?"—watch realtime magic!
This setup delivers sub-second RAG latency globally. Scale to enterprise with Supabase Pro.
Word count: ~1450
Comments
More Blog
View allBuilding Voice Agents with Claude API and ElevenLabs: Conversational AI Guide
Build natural voice agents combining Claude API's superior reasoning with ElevenLabs' lifelike TTS. This end-to-end guide creates a conversational web app with STT, AI chat, and speech synthesis.
Claude vs Mistral Large 2: 2025 Data Analysis Benchmarks and Use Cases
As data volumes explode in 2025, choosing between Claude's reasoning depth and Mistral Large 2's efficiency is critical. We benchmark SQL generation, visualizations, and large datasets to reveal the w
Claude Enterprise for Cybersecurity: Threat Modeling and Incident Response
In the high-stakes world of cybersecurity, rapid threat modeling and incident response can mean the difference between containment and catastrophe. Discover how Claude Enterprise empowers security tea
Claude Code in VS Code: Custom Commands for Refactoring Large Codebases
Refactoring sprawling codebases manually? Harness Claude Code's power in VS Code with custom commands to automate AI-driven refactors across TypeScript and Python projects—saving hours of drudgery.
Claude SDK Rust for Blockchain: Smart Contract Auditing Agents
Build blazing-fast smart contract auditing agents in Rust using the Claude SDK. Harness Claude's reasoning to scan Solidity code for vulnerabilities like reentrancy and overflows.
Advanced Claude Artifacts: Collaborative Editing in Multi-User Sessions
Elevate team productivity with Claude Artifacts in multi-user projects—enable real-time iterative editing for code reviews and docs without leaving the interface.