Tablas y Schema
Una tabla guarda un tipo de cosa — productos, pedidos, mensajes, facturas. Cada columna es un dato sobre esa cosa (un precio, una fecha, un user ID). Tener el schema bien desde el principio te ahorra dolores después.
Crear una tabla
Abre la pestaña Database
En el dashboard de tu proyecto, haz clic en Database en el sidebar.
Haz clic en New Table
Dale a la tabla un nombre en minúsculas y plural — products, orders, messages. Usa snake_case.
Añade columnas
El editor crea una clave primaria id de tipo uuid. Añade lo demás que necesites — incluidos los timestamps created_at / updated_at, que pones tú mismo (ver Timestamps más abajo).
Guarda
La tabla se crea al momento. Puedes añadir o quitar columnas después.

O simplemente pídeselo a tu IA
Tu cliente de IA puede crear y modificar tablas por ti vía MCP — aplica los cambios de schema como migraciones (apply_migration), así que se ejecutan de forma segura. Describe la tabla en lenguaje natural y deja que escriba el SQL.
Tipos de columna
El selector de tipos del editor visual cubre los tipos de PostgreSQL más comunes. Los que más usarás:
| Tipo | Úsalo para | Ejemplo |
|---|---|---|
text | Nombres, descripciones, cualquier string | "Camiseta azul de algodón" |
varchar(n) | Strings con límite duro de longitud | varchar(50) para un username |
integer / bigint / smallint | Conteos, números pequeños, IDs | 42 |
numeric(10,2) | Dinero, decimales exactos | 199.99 |
double precision / real | Decimales aproximados (física, analítica) | 3.14159 |
boolean | Flags sí/no | is_active, email_verified |
timestamptz / timestamp | Fechas + horas (con / sin zona horaria) | 2026-04-16 10:30:00+00 |
date / time | Solo una fecha / solo una hora | 2026-04-16 |
uuid | IDs únicos globales | 550e8400-e29b-41d4-... |
jsonb / json | Datos anidados o flexibles | {"color": "red", "size": "M"} |
bytea | Binario crudo | (blobs pequeños) |
Los arrays son solo por SQL
Tipos array como text[] son PostgreSQL válido y funcionan genial, pero no están en el selector visual de tipos — añádelos desde el editor SQL (o pídeselo a tu IA). Todo lo que está en la tabla de arriba sí está disponible en el editor visual.
En la duda, prefiere text a varchar
varchar(n) parece más seguro pero text es idéntico en rendimiento y no rompe cuando necesitas ese carácter extra. Usa varchar solo cuando la longitud máxima sea una regla de negocio real.
Claves primarias
Usa uuid con un default. Es aleatoria, segura contra colisiones y no filtra el conteo de filas:
id UUID PRIMARY KEY DEFAULT gen_random_uuid()
Usa bigserial (entero autoincremental) solo cuando necesites orden de verdad o URLs más cortas. Para la mayoría de apps, UUID es la respuesta correcta.
Timestamps
Cada tabla debería tener estas dos columnas:
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
El editor visual crea la clave primaria id pero no los timestamps — añade created_at / updated_at tú mismo (en el selector de columnas o por SQL). Las querrás para debugging, ordenar y audit trails después, así que vale la pena añadirlas en cada tabla.
Para mantener updated_at actualizado, o lo actualizas en tu workflow (SET updated_at = now()) o añades un trigger. La mayoría de apps lo actualizan en el workflow — más simple.
⚠️ Referencias a usuarios: usa TEXT, no UUID
Este es el error #1 que comete la gente. El sistema de auth de DYPAI usa un ID de 32 caracteres (no un UUID), así que cualquier columna que apunte a un usuario debe ser TEXT.
Correcto
CREATE TABLE public.tasks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id TEXT NOT NULL REFERENCES system.users(id) ON DELETE CASCADE,
title TEXT NOT NULL,
done BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
Incorrecto (rompe en runtime)
-- ❌ No hagas esto
user_id UUID REFERENCES system.users(id)
Cuando un endpoint intente WHERE user_id = ${current_user_id}, Postgres falla con:
invalid input syntax for type uuid: "G1LIBXsbMLxUrs99ebCaL9X4auxW26AC"
Regla: cualquier columna que enlaza una fila con un usuario = TEXT. Otras claves foráneas (product_id → products.id, order_id → orders.id) mantienen su UUID natural.
En los placeholders del workflow
Escribe WHERE user_id = ${current_user_id} — sin comillas, sin cast ::uuid. El engine bindea el parámetro con el tipo correcto automáticamente.
Índices
Añade un índice a cualquier columna por la que filtres u ordenes a menudo. El editor visual tiene un panel Indexes separado por tabla:

Patrones comunes:
- Indexa cada clave foránea (
user_id,product_id, etc.) — la mayoría de queries filtran por ellas - Indexa
created_at DESCsi listas items recientes - Sáltate índices en tablas pequeñas (menos de ~1k filas) — Postgres es suficientemente rápido sin ellos
Los índices cuestan en escrituras
Cada índice hace los inserts y updates un poco más lentos. Añádelos cuando las queries vayan lentas, no preventivamente.
Ejemplo: una tabla tasks bien modelada
Así queda una tabla típica de un usuario con todas las convenciones aplicadas:
CREATE TABLE public.tasks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id TEXT NOT NULL REFERENCES system.users(id) ON DELETE CASCADE,
title TEXT NOT NULL,
description TEXT,
done BOOLEAN NOT NULL DEFAULT false,
due_date DATE,
priority INT NOT NULL DEFAULT 0,
tags TEXT[] NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_tasks_user_id ON public.tasks(user_id);
CREATE INDEX idx_tasks_created_at ON public.tasks(created_at DESC);
Copia esto como punto de partida cuando construyas algo que pertenece a un usuario.