Migraciones de Base de Datos en Supabase
Las migraciones son archivos SQL que describen cambios en tu esquema de base de datos de forma incremental y ordenada. Cada migración captura un cambio específico: crear una tabla, agregar una columna, definir una política de RLS. Cuando las aplicas en orden, reconstruyen tu base de datos desde cero hasta el estado actual.
Por qué usar migraciones
Sin migraciones, los cambios en tu base de datos viven solo en el dashboard. Si trabajas en equipo, cada dev tendría que replicar los cambios manualmente. Si necesitas recrear la base de datos, tienes que recordar cada paso.
Con migraciones:
- Control de versiones: cada cambio es un archivo SQL que va al repositorio
- Reproducibilidad: cualquiera puede recrear el esquema completo desde cero
- Colaboración: los cambios de esquema se revisan en PRs como cualquier otro código
- Rollback: puedes revertir cambios si algo sale mal
- Automatización: CI/CD puede aplicar migraciones automáticamente
Crear una migración
Desde el CLI (manual)
supabase migration new crear_tabla_productosEsto crea un archivo vacío en supabase/migrations/:
supabase/migrations/20250312143000_crear_tabla_productos.sqlAhora escribes el SQL del cambio:
-- supabase/migrations/20250312143000_crear_tabla_productos.sql
CREATE TABLE productos (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
nombre TEXT NOT NULL,
descripcion TEXT,
precio NUMERIC(10, 2) NOT NULL CHECK (precio >= 0),
stock INTEGER NOT NULL DEFAULT 0 CHECK (stock >= 0),
categoria TEXT,
activo BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Habilitar RLS
ALTER TABLE productos ENABLE ROW LEVEL SECURITY;
-- Política de lectura pública
CREATE POLICY "Productos visibles para todos"
ON productos FOR SELECT
USING (activo = true);
-- Indice para busquedas por categoria
CREATE INDEX idx_productos_categoria ON productos (categoria);Desde db diff (automático)
Si ya hiciste cambios en tu base de datos local usando Studio:
supabase db diff --use-migra -f agregar_columna_imagenEl CLI detecta las diferencias y genera el SQL automáticamente. Revisa siempre el archivo generado -- db diff a veces genera SQL innecesario o en un orden inesperado.
Estructura del archivo de migración
El nombre del archivo sigue el formato:
YYYYMMDDHHMMSS_nombre_descriptivo.sqlEl timestamp garantiza que las migraciones se ejecuten en orden cronológico. Esto es importante porque una migración puede depender de otra anterior (no puedes agregar una columna a una tabla que no existe todavía).
Convenciones para nombres
| Acción | Nombre sugerido |
|---|---|
| Crear tabla | crear_tabla_productos |
| Agregar columna | agregar_imagen_a_productos |
| Crear índice | indice_busqueda_productos |
| Agregar RLS | rls_productos_lectura |
| Crear función | funcion_calcular_total |
| Modificar tipo | cambiar_precio_a_numeric |
Aplicar migraciones
En tu base de datos local
supabase db resetEste comando:
- Borra toda la base de datos local
- Re-ejecuta todas las migraciones en orden
- Ejecuta el archivo
seed.sqlsi existe
Es destructivo pero necesario para verificar que tus migraciones funcionan desde cero.
db reset borra todo
supabase db reset elimina todos los datos de tu base de datos local. Úsalo solo en desarrollo. Tus datos de prueba se pierden a menos que los tengas en seed.sql.
En el proyecto remoto (producción)
supabase db pushEste comando aplica las migraciones pendientes (las que todavía no se ejecutaron en el proyecto remoto). No borra datos, no hace reset -- solo ejecuta las migraciones nuevas.
# Ver que migraciones se van a aplicar antes de hacerlo
supabase db push --dry-runAntes de pushear a producción
Siempre haz un --dry-run primero. Revisa el SQL que se va a ejecutar. Una migración mal escrita puede borrar datos o romper tu esquema en producción.
Rollback de migraciones
Supabase no tiene un mecanismo automático de rollback. Si una migración causa problemas, tienes que crear una nueva migración que revierta los cambios.
Ejemplo de migración de rollback
Si la migración original agregó una columna:
-- 20250312143000_agregar_columna_imagen.sql
ALTER TABLE productos ADD COLUMN imagen_url TEXT;La migración de rollback la elimina:
-- 20250312150000_revertir_columna_imagen.sql
ALTER TABLE productos DROP COLUMN imagen_url;Patrón recomendado
Cuando escribas migraciones, piensa si la puedes revertir fácilmente. Crear una tabla se revierte con DROP TABLE. Agregar una columna se revierte con DROP COLUMN. Pero borrar datos no tiene reversión -- esos datos se pierden.
Revertir localmente
Si quieres deshacer la última migración en tu entorno local, borra el archivo de migración y resetea:
# Borrar el archivo de la ultima migracion
rm supabase/migrations/20250312150000_migracion_mala.sql
# Resetear para aplicar todas las migraciones menos la borrada
supabase db resetSeed data
El archivo supabase/seed.sql contiene datos iniciales que se cargan después de ejecutar todas las migraciones. Es útil para datos de prueba en desarrollo.
-- supabase/seed.sql
-- Categorias por defecto
INSERT INTO categorias (nombre, slug) VALUES
('Electronica', 'electronica'),
('Ropa', 'ropa'),
('Hogar', 'hogar'),
('Deportes', 'deportes');
-- Productos de prueba
INSERT INTO productos (nombre, precio, stock, categoria) VALUES
('Laptop Pro 15', 1299.99, 50, 'electronica'),
('Mouse Inalambrico', 29.99, 200, 'electronica'),
('Camiseta Básica', 19.99, 500, 'ropa'),
('Silla Ergonomica', 349.99, 30, 'hogar'),
('Pelota de Futbol', 24.99, 100, 'deportes');
-- Usuario de prueba (para auth, usa el SQL de GoTrue)
-- Esto depende de tu setup de authEl seed se ejecuta automáticamente con supabase db reset. No se ejecuta con supabase db push (no quieres cargar datos de prueba en producción).
Workflow en equipo
Cuando trabajas con más personas, las migraciones son la única fuente de verdad del esquema. El flujo es:
1. Cada dev trabaja en local
# Dev A crea una tabla
supabase migration new crear_tabla_pedidos
# Escribe el SQL
# Prueba localmente con supabase db reset2. Push al repositorio
git add supabase/migrations/20250312143000_crear_tabla_pedidos.sql
git commit -m "feat: crear tabla pedidos"
git push3. Otro dev sincroniza
git pull
supabase db reset # Aplica todas las migraciones incluyendo la nueva4. Deploy a producción
supabase db push # Solo aplica las migraciones nuevasConflictos de migración
Si dos devs crean migraciones al mismo tiempo, puede haber conflictos. Por ejemplo:
- Dev A crea
20250312140000_tabla_pedidos.sql - Dev B crea
20250312140500_tabla_envios.sql - Dev B agrega una foreign key de
enviosapedidos
Si la migración de Dev B se aplica antes que la de Dev A, falla porque la tabla pedidos no existe todavía.
La solución: revisar el orden de las migraciones después de un merge. Si hay dependencias, renombra el timestamp para que el orden sea correcto.
Una migración por PR
Como buena práctica, cada PR debería incluir como máximo una migración. Esto hace que los cambios de esquema sean fáciles de revisar y de revertir si algo sale mal.
Migraciones comunes
Crear tabla con RLS
CREATE TABLE pedidos (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
usuario_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
total NUMERIC(10, 2) NOT NULL,
estado TEXT NOT NULL DEFAULT 'pendiente',
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE pedidos ENABLE ROW LEVEL SECURITY;
-- Los usuarios solo ven sus propios pedidos
CREATE POLICY "Usuarios ven sus pedidos"
ON pedidos FOR SELECT
USING (auth.uid() = usuario_id);
-- Los usuarios solo crean pedidos para si mismos
CREATE POLICY "Usuarios crean sus pedidos"
ON pedidos FOR INSERT
WITH CHECK (auth.uid() = usuario_id);Agregar columna con valor por defecto
ALTER TABLE productos
ADD COLUMN imagen_url TEXT DEFAULT '';
-- Actualizar registros existentes
UPDATE productos
SET imagen_url = 'https://placeholder.com/default.jpg'
WHERE imagen_url = '';Crear función y trigger
-- Función que actualiza updated_at automáticamente
CREATE OR REPLACE FUNCTION actualizar_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Aplicar a la tabla productos
CREATE TRIGGER trigger_updated_at
BEFORE UPDATE ON productos
FOR EACH ROW
EXECUTE FUNCTION actualizar_updated_at();Crear índice para performance
-- Indice simple
CREATE INDEX idx_pedidos_usuario ON pedidos (usuario_id);
-- Indice compuesto
CREATE INDEX idx_pedidos_usuario_estado ON pedidos (usuario_id, estado);
-- Indice para busqueda de texto
CREATE INDEX idx_productos_nombre_search
ON productos USING GIN (to_tsvector('spanish', nombre));Resumen
| Tarea | Comando |
|---|---|
| Crear migración vacía | supabase migration new nombre |
| Generar migración desde diff | supabase db diff --use-migra -f nombre |
| Aplicar en local (reset) | supabase db reset |
| Aplicar en producción | supabase db push |
| Preview antes de push | supabase db push --dry-run |
| Bajar esquema remoto | supabase db pull |
| Seed data | Archivo supabase/seed.sql |
| Rollback | Crear nueva migración que revierte los cambios |