BackendAvanzado55 min
Supabase + IA: El Stack Completo
pgvector · Edge Functions · Realtime · Auth · todo conectado con Claude
SupabasepgvectorEdge Functions
✓ Guía gratuita · Acceso completo
Mapa rápido · salta al paso que te interese
01
Setup Supabase para IA
Supabase es la opción ideal para backends de IA: Postgres con pgvector, Edge Functions en Deno, Auth, y Realtime. Todo en un lugar.
Habilitar pgvector y crear tablas
-- 1. Habilitar extensiones
create extension if not exists vector;
create extension if not exists pg_trgm; -- para búsqueda de texto
-- 2. Tabla de documentos con embeddings
create table documents (
id uuid primary key default gen_random_uuid(),
user_id uuid references auth.users(id) on delete cascade,
title text not null,
content text not null,
metadata jsonb default '{}',
embedding vector(1536),
created_at timestamptz default now(),
updated_at timestamptz default now()
);
-- 3. Tabla de conversaciones
create table conversations (
id uuid primary key default gen_random_uuid(),
user_id uuid references auth.users(id) on delete cascade,
title text,
created_at timestamptz default now()
);
-- 4. Tabla de mensajes
create table messages (
id uuid primary key default gen_random_uuid(),
conversation_id uuid references conversations(id) on delete cascade,
role text check (role in ('user', 'assistant', 'system')),
content text not null,
tokens_used int,
created_at timestamptz default now()
);
-- 5. Índices
create index on documents using ivfflat (embedding vector_cosine_ops) with (lists = 100);
create index on documents using gin (to_tsvector('spanish', content));
create index on messages (conversation_id, created_at);Row Level Security (RLS)
-- Users solo ven sus propios documentos
alter table documents enable row level security;
create policy "Users can CRUD own documents"
on documents for all
using (auth.uid() = user_id)
with check (auth.uid() = user_id);
-- Conversaciones
alter table conversations enable row level security;
create policy "Users can CRUD own conversations"
on conversations for all
using (auth.uid() = user_id)
with check (auth.uid() = user_id);
-- Mensajes (via conversation ownership)
alter table messages enable row level security;
create policy "Users can CRUD messages in own conversations"
on messages for all
using (
conversation_id in (
select id from conversations where user_id = auth.uid()
)
);Función de búsqueda semántica
create or replace function search_documents(
query_embedding vector(1536),
match_count int default 5,
filter_user_id uuid default null
)
returns table (
id uuid,
title text,
content text,
metadata jsonb,
similarity float
)
language plpgsql
as $$
begin
return query
select
d.id,
d.title,
d.content,
d.metadata,
1 - (d.embedding <=> query_embedding) as similarity
from documents d
where
(filter_user_id is null or d.user_id = filter_user_id)
and d.embedding is not null
order by d.embedding <=> query_embedding
limit match_count;
end;
$$;02
Edge Functions + Claude
Edge Functions corren en Deno cerca del usuario. Perfectas para llamadas a Claude con baja latencia.
supabase/functions/chat/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'
import Anthropic from 'https://esm.sh/@anthropic-ai/sdk'
const anthropic = new Anthropic({
apiKey: Deno.env.get('ANTHROPIC_API_KEY')!,
})
serve(async (req) => {
// CORS
if (req.method === 'OPTIONS') {
return new Response('ok', {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, content-type',
},
})
}
try {
// Auth
const authHeader = req.headers.get('Authorization')!
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_ANON_KEY')!,
{ global: { headers: { Authorization: authHeader } } }
)
const { data: { user }, error: authError } = await supabase.auth.getUser()
if (authError || !user) {
return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401 })
}
// Parse request
const { message, conversationId } = await req.json()
// Get conversation history
const { data: history } = await supabase
.from('messages')
.select('role, content')
.eq('conversation_id', conversationId)
.order('created_at', { ascending: true })
.limit(20)
// Build messages
const messages = [
...(history || []).map(m => ({ role: m.role, content: m.content })),
{ role: 'user', content: message }
]
// Call Claude
const response = await anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 2048,
system: 'Eres un asistente útil y conciso.',
messages,
})
const assistantMessage = response.content[0].text
// Save messages
await supabase.from('messages').insert([
{ conversation_id: conversationId, role: 'user', content: message },
{ conversation_id: conversationId, role: 'assistant', content: assistantMessage, tokens_used: response.usage.output_tokens },
])
return new Response(JSON.stringify({
message: assistantMessage,
usage: response.usage,
}), {
headers: { 'Content-Type': 'application/json' },
})
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), { status: 500 })
}
})Tip Pro
Deploy con:
supabase functions deploy chat03
Realtime para streaming
Supabase Realtime permite que el frontend reciba updates instantáneos cuando el backend inserta mensajes.
Frontend: suscribirse a mensajes
import { useEffect, useState } from 'react'
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY)
function useConversation(conversationId: string) {
const [messages, setMessages] = useState<Message[]>([])
useEffect(() => {
// Load initial messages
supabase
.from('messages')
.select('*')
.eq('conversation_id', conversationId)
.order('created_at')
.then(({ data }) => setMessages(data || []))
// Subscribe to new messages
const channel = supabase
.channel(`conversation:${conversationId}`)
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'messages',
filter: `conversation_id=eq.${conversationId}`,
},
(payload) => {
setMessages(prev => [...prev, payload.new as Message])
}
)
.subscribe()
return () => {
supabase.removeChannel(channel)
}
}, [conversationId])
return messages
}⚠ Advertencia
Realtime tiene límites en el free tier. 200 conexiones concurrentes. Pro tier: ilimitadas.
"Supabase es el backend que Firebase prometió pero nunca entregó. pgvector, Edge Functions, Realtime, Auth — todo lo que necesitás para una app de IA en un solo lugar."
— El Arsenal, Renderz Studio
Guías relacionadas
¿Quieres el sistema completo?
500+ páginas, 25 capítulos, código de producción, acceso a todas las guías del Arsenal.
Curso Completo · €29PayPal · Acceso inmediato