Claude Code + Supabase: KI-Backend mit Vector Search und RAG 2026

Warum für jede KI-App vier verschiedene Services konfigurieren, wenn Supabase Auth, PostgreSQL, pgvector und Storage in einem bündelt? Claude Code richtet das komplette Backend in Minuten ein — inklusive RLS-Policies, Migrations und einem produktionsreifen RAG-System.

Supabase: Das All-in-One Backend für KI-Applikationen

Wer heute eine KI-Applikation baut, braucht typischerweise: eine relationale Datenbank für strukturierte Daten, eine Vektordatenbank für Embeddings, ein Auth-System für Benutzer und Object Storage für Dateien. In der Praxis bedeutet das vier verschiedene Services, vier verschiedene SDKs, vier verschiedene Billing-Konten.

Supabase löst genau dieses Problem. Auf Basis von PostgreSQL — der zuverlässigsten Open-Source-Datenbank der Welt — bündelt Supabase alles in einem Stack. Das pgvector-Extension macht die PostgreSQL-Instanz zur Vektordatenbank, GoTrue übernimmt Authentication, und ein S3-kompatibles Storage-Layer rundet das Paket ab.

AuthGoTrue, JWT, OAuth Providers
DatabasePostgreSQL 15+, Row Level Security
Vectorpgvector, ivfflat, hnsw Index
StorageS3-kompatibel, CDN, Transforms
RealtimeWebSocket, Broadcast, Presence
Edge FunctionsDeno Runtime, weltweit verteilt
Für Claude Code ideal: Supabase hat eine hervorragende TypeScript-Typisierung. Claude Code generiert typsichere Datenbankoperationen, die exakt zur Datenbankstruktur passen — ohne Laufzeitfehler durch falsche Spaltennamen.

Claude Code Setup mit Supabase: Client, RLS und Migrations

Der erste Schritt ist immer die saubere Einrichtung des Supabase-Clients und der Row-Level-Security-Policies. Claude Code kennt die Supabase-Konventionen und generiert Migrations-Dateien, die direkt mit supabase db push eingespielt werden können.

Supabase Client initialisieren + Typen generieren

# Prompt: "Richte Supabase in einem Next.js-Projekt ein: # - Server-Side und Client-Side Client # - Typen aus der Datenbank generieren # - Environment-Variables validieren beim Start" ## Terminal: Typen generieren npx supabase gen types typescript \ --project-id $SUPABASE_PROJECT_ID \ --schema public > src/types/supabase.ts ## src/lib/supabase/server.ts import { createServerClient } from '@supabase/ssr' import { cookies } from 'next/headers' import type { Database } from '@/types/supabase' export function createSupabaseServerClient() { const cookieStore = cookies() return createServerClient<Database>( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, { cookies: { get(name) { return cookieStore.get(name)?.value }, set(name, value, options) { cookieStore.set({ name, value, ...options }) }, remove(name, options) { cookieStore.set({ name, value: '', ...options }) } } } ) } ## src/lib/supabase/admin.ts — nur serverseitig! import { createClient } from '@supabase/supabase-js' export const supabaseAdmin = createClient<Database>( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.SUPABASE_SERVICE_ROLE_KEY! // NIEMALS im Client-Bundle! )

RLS-Policies und Migrations mit Claude Code

# Prompt: "Erstelle eine Migration für eine documents-Tabelle # mit RLS: User kann nur eigene Dokumente lesen und schreiben." -- supabase/migrations/20260505_documents.sql CREATE TABLE documents ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL, title TEXT NOT NULL, content TEXT, created_at TIMESTAMPTZ DEFAULT now() ); -- Row Level Security aktivieren ALTER TABLE documents ENABLE ROW LEVEL SECURITY; -- Policy: nur eigene Zeilen lesen CREATE POLICY "users_select_own" ON documents FOR SELECT USING (auth.uid() = user_id); -- Policy: nur eigene Zeilen einfügen CREATE POLICY "users_insert_own" ON documents FOR INSERT WITH CHECK (auth.uid() = user_id); -- Policy: nur eigene Zeilen aktualisieren CREATE POLICY "users_update_own" ON documents FOR UPDATE USING (auth.uid() = user_id); -- Policy: nur eigene Zeilen löschen CREATE POLICY "users_delete_own" ON documents FOR DELETE USING (auth.uid() = user_id);
Wichtig: Service Role Key schützen! Der SUPABASE_SERVICE_ROLE_KEY umgeht alle RLS-Policies. Er darf ausschließlich in Server-Side-Code verwendet werden — niemals im Browser-Bundle oder in Next.js Client Components.

Vector Search mit pgvector: Embeddings speichern und Similarity Search

pgvector ist eine PostgreSQL-Extension, die Vektor-Operationen direkt in der Datenbank ermöglicht. Statt eine separate Vektordatenbank wie Pinecone oder Weaviate zu betreiben, laufen Embedding-Storage und Similarity Search auf derselben PostgreSQL-Instanz — mit allen Vorteilen von ACID-Transaktionen und RLS.

pgvector aktivieren und Embeddings speichern

# Prompt: "Aktiviere pgvector und erstelle eine Tabelle für # Text-Embeddings (1536 Dimensionen für OpenAI text-embedding-3-small). # Erstelle einen ivfflat-Index für schnelle Similarity Search." -- Migration: pgvector aktivieren CREATE EXTENSION IF NOT EXISTS vector; -- Embeddings-Tabelle CREATE TABLE document_chunks ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, document_id UUID REFERENCES documents(id) ON DELETE CASCADE, user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE, chunk_index INTEGER NOT NULL, content TEXT NOT NULL, embedding vector(1536), -- OpenAI text-embedding-3-small metadata JSONB DEFAULT '{}', created_at TIMESTAMPTZ DEFAULT now() ); -- ivfflat-Index (schnell bei >10k Vektoren) CREATE INDEX ON document_chunks USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100); -- RLS wie bei der documents-Tabelle ALTER TABLE document_chunks ENABLE ROW LEVEL SECURITY; CREATE POLICY "chunks_select_own" ON document_chunks FOR SELECT USING (auth.uid() = user_id);

Similarity Search als PostgreSQL-Funktion

-- Prompt: "Erstelle eine match_chunks-Funktion die: -- - einen Query-Embedding-Vektor nimmt -- - die k ähnlichsten Chunks zurückgibt -- - nach Cosine Similarity sortiert (0.0 bis 1.0)" CREATE OR REPLACE FUNCTION match_chunks ( query_embedding vector(1536), match_count INT DEFAULT 5, similarity_threshold FLOAT DEFAULT 0.7 ) RETURNS TABLE ( id UUID, content TEXT, similarity FLOAT, metadata JSONB ) LANGUAGE SQL STABLE AS $$ SELECT id, content, 1 - (embedding <=> query_embedding) AS similarity, metadata FROM document_chunks WHERE user_id = auth.uid() AND 1 - (embedding <=> query_embedding) > similarity_threshold ORDER BY embedding <=> query_embedding LIMIT match_count; $$;
ivfflat vs. hnsw: Für weniger als 100.000 Vektoren ist ivfflat mit lists = sqrt(Anzahl_Vektoren) optimal. Bei mehr als 500.000 Vektoren lohnt sich der hnsw-Index — er ist langsamer beim Einfügen, aber deutlich schneller bei der Suche.

RAG-System mit Supabase bauen: Vom Dokument zur Antwort

Retrieval-Augmented Generation (RAG) ist das Herzstück moderner KI-Apps: Dokumente werden in Chunks zerlegt, als Embeddings gespeichert, und beim Query werden die semantisch ähnlichsten Chunks als Kontext an das Sprachmodell übergeben. Supabase ist dafür ideal — Storage, Embedding-Storage und Retrieval auf einer Plattform.

Schritt 1: Dokumente chunken und embedden

# Prompt: "Implementiere eine ingestDocument-Funktion die: # 1. Dokument in 512-Zeichen-Chunks aufteilt (100 Zeichen Overlap) # 2. Jeden Chunk mit OpenAI embeddet # 3. Chunks mit Embeddings in Supabase speichert" import { OpenAI } from 'openai' import { createSupabaseServerClient } from '@/lib/supabase/server' const openai = new OpenAI() const CHUNK_SIZE = 512 const CHUNK_OVERLAP = 100 function chunkText(text: string): string[] { const chunks: string[] = [] let start = 0 while (start < text.length) { const end = Math.min(start + CHUNK_SIZE, text.length) chunks.push(text.slice(start, end)) start += CHUNK_SIZE - CHUNK_OVERLAP } return chunks } export async function ingestDocument(documentId: string, content: string) { const supabase = createSupabaseServerClient() const chunks = chunkText(content) // Embeddings in Batches von 100 generieren (Rate-Limit beachten) const embeddings = await openai.embeddings.create({ model: 'text-embedding-3-small', input: chunks, dimensions: 1536 }) // Alle Chunks in einer Transaktion speichern const chunkRows = chunks.map((chunk, i) => ({ document_id: documentId, chunk_index: i, content: chunk, embedding: embeddings.data[i].embedding, metadata: { char_start: i * (CHUNK_SIZE - CHUNK_OVERLAP) } })) const { error } = await supabase .from('document_chunks') .insert(chunkRows) if (error) throw new Error(`Ingest failed: ${error.message}`) return { chunksCreated: chunks.length } }

Schritt 2: Retrieval und Antwort generieren

# Prompt: "Implementiere die RAG-Query-Funktion: # 1. Query embedden # 2. Ähnlichste Chunks aus Supabase abrufen (match_chunks RPC) # 3. Als Kontext an Claude übergeben, Antwort streamen" import Anthropic from '@anthropic-ai/sdk' const anthropic = new Anthropic() export async function ragQuery(query: string) { const supabase = createSupabaseServerClient() // 1. Query embedden const { data: [{ embedding }] } = await openai.embeddings.create({ model: 'text-embedding-3-small', input: query, dimensions: 1536 }) // 2. Ähnlichste Chunks abrufen (via match_chunks RPC) const { data: chunks, error } = await supabase .rpc('match_chunks', { query_embedding: embedding, match_count: 5, similarity_threshold: 0.7 }) if (error) throw error // 3. Kontext aufbauen const context = chunks .map((c, i) => `[Quelle ${i+1}] ${c.content}`) .join('\n\n') // 4. Claude mit Kontext befragen (streaming) const stream = await anthropic.messages.stream({ model: 'claude-opus-4-5', max_tokens: 1024, messages: [{ role: 'user', content: `Beantworte die Frage ausschließlich basierend auf diesem Kontext: ${context} Frage: ${query}` }] }) return stream // Stream direkt an Client weitergeben }
RAG-Qualität verbessern: Claude Code hilft beim Prompt-Engineering für RAG. Nützlicher Zusatz: "Wenn die Antwort nicht im Kontext enthalten ist, sage das explizit — halluziniere keine Fakten." Dieser einfache Zusatz reduziert Halluzinationen drastisch.

Realtime Subscriptions: Live-Updates in KI-Apps

Supabase Realtime ermöglicht WebSocket-basierte Live-Updates direkt aus der PostgreSQL-Datenbank. Für KI-Apps ideal: wenn ein Hintergrundprozess Embeddings generiert, können Clients in Echtzeit über den Fortschritt informiert werden.

Realtime Subscription auf Dokument-Status

# Prompt: "Implementiere Realtime-Updates für den Ingestion-Status. # Wenn status sich zu 'ready' ändert, soll die UI automatisch aktualisieren." // src/hooks/useDocumentStatus.ts import { useEffect, useState } from 'react' import { createSupabaseBrowserClient } from '@/lib/supabase/client' export function useDocumentStatus(documentId: string) { const [status, setStatus] = useState<'processing' | 'ready' | 'error'>('processing') const supabase = createSupabaseBrowserClient() useEffect(() => { // Aktuellen Status laden supabase .from('documents') .select('status') .eq('id', documentId) .single() .then(({ data }) => data && setStatus(data.status)) // Realtime-Subscription auf Status-Änderungen const channel = supabase .channel(`document-${documentId}`) .on( 'postgres_changes', { event: 'UPDATE', schema: 'public', table: 'documents', filter: `id=eq.${documentId}` }, (payload) => setStatus(payload.new.status) ) .subscribe() // Cleanup beim Unmount return () => { supabase.removeChannel(channel) } }, [documentId]) return status }
Realtime und RLS: Supabase Realtime respektiert Row-Level-Security. Clients empfangen nur Updates für Zeilen, auf die sie gemäß ihrer RLS-Policies Lesezugriff haben. Kein zusätzlicher Sicherheits-Layer nötig.

Edge Functions: Supabase Serverless + Claude API direkt

Supabase Edge Functions laufen in Deno auf Cloudflare Workers — weltweit verteilt mit kalten Startzeiten unter 50ms. Für KI-Apps perfekt: Embedding-Generierung und Claude-API-Calls können direkt in der Edge Function stattfinden, ohne eigenen Server.

Edge Function: Automatisches Embedden bei Dokument-Upload

# Prompt: "Erstelle eine Supabase Edge Function die beim INSERT # in die documents-Tabelle (via Database Webhook) ausgelöst wird # und das Dokument automatisch chunked und embeddet." // supabase/functions/embed-document/index.ts import { serve } from 'https://deno.land/std@0.168.0/http/server.ts' import { createClient } from 'https://esm.sh/@supabase/supabase-js@2' serve(async (req) => { const { record } = await req.json() // Webhook-Payload const supabase = createClient( Deno.env.get('SUPABASE_URL')!, Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')! ) // Dokument-Content aus Storage holen (falls PDF/Datei) const content = record.content ?? await fetchFromStorage(supabase, record.storage_path) // Embeddings via OpenAI generieren const openaiResponse = await fetch('https://api.openai.com/v1/embeddings', { method: 'POST', headers: { 'Authorization': `Bearer ${Deno.env.get('OPENAI_API_KEY')}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'text-embedding-3-small', input: chunkText(content) }) }) const { data: embeddings } = await openaiResponse.json() // Chunks in Supabase speichern const chunks = chunkText(content).map((chunk, i) => ({ document_id: record.id, user_id: record.user_id, chunk_index: i, content: chunk, embedding: embeddings[i].embedding })) await supabase.from('document_chunks').insert(chunks) // Status auf 'ready' setzen await supabase.from('documents') .update({ status: 'ready' }) .eq('id', record.id) return new Response(JSON.stringify({ ok: true })) })

Edge Function deployen und Webhook verbinden

# Deployment npx supabase functions deploy embed-document --no-verify-jwt # Secrets setzen npx supabase secrets set OPENAI_API_KEY=sk-... # Database Webhook in Supabase Dashboard: # Table: documents | Event: INSERT # URL: https://[project-id].supabase.co/functions/v1/embed-document # HTTP Method: POST # Lokale Entwicklung mit hot-reload: npx supabase functions serve embed-document --env-file .env.local

Produktions-Checkliste: Claude Code + Supabase

  • RLS auf allen Tabellen: Kein ALTER TABLE ... DISABLE ROW LEVEL SECURITY in Produktionsmigrations.
  • Service Role Key sichern: Nur in Server-Side-Code, nie im Client-Bundle oder in Git ohne .gitignore.
  • pgvector Index wählen: ivfflat für <100k Vektoren, hnsw für größere Datenmengen.
  • Embedding-Dimensionen konsistent: Modell und Spaltendefinition müssen übereinstimmen — Mismatch gibt erst zur Laufzeit Fehler.
  • Chunk-Overlap bei RAG: Mindestens 10% Overlap zwischen Chunks, sonst gehen Satz-Grenzen verloren.
  • Realtime-Subscriptions aufräumen: supabase.removeChannel() im Cleanup-Handler — sonst Memory-Leaks.
  • Edge Functions mit Tests: supabase functions serve + lokale Integration-Tests vor Deployment.
Supabase Free Tier Limits: 500 MB Datenbank, 1 GB Storage, 2 GB Bandbreite. Für ein produktives RAG-System mit vielen Dokumenten ist das Pro-Tier (€25/Monat) schnell nötig. pgvector benötigt zusätzlichen Arbeitsspeicher — das Free-Tier-Limit von 500 MB RAM kann bei größeren Index-Operationen ein Bottleneck werden.

Fazit: Supabase ist das ideale KI-Backend 2026

Supabase kombiniert alles, was eine moderne KI-Applikation braucht: PostgreSQL als zuverlässige Datenbasis, pgvector für Embeddings und Similarity Search, GoTrue für Authentication mit RLS-Integration, Realtime für Live-Updates, Edge Functions für serverlose KI-Workflows — und das alles hinter einem einzigen SDK.

Claude Code kennt die Supabase-Konventionen in und auswendig: korrekte RLS-Policies, typsichere Datenbankoperationen, optimale Index-Konfigurationen für pgvector. Was früher Tage dauerte — ein vollständiges Backend mit Vector Search aufzusetzen — ist mit Claude Code und Supabase eine Angelegenheit von Stunden.

Vollständiges Supabase + RAG-Modul im Kurs

Im Claude Code Mastery Kurs gibt es ein komplettes Supabase-Modul: RLS-Patterns, pgvector-Setup, RAG-System von Grund auf, Edge Functions und Realtime Subscriptions — mit produktionsreifem Code und Migrations.

14 Tage kostenlos testen →