Añadir una tabla al schema
Crear una tabla Drizzle, registrarla y generar/aplicar la migración en las bases de dev y de producción.
La base de datos es PostgreSQL con Drizzle ORM. Los esquemas viven en
apps/api/src/db/schema/, organizados por dominio (un archivo por dominio). Drizzle Kit
lee todos los archivos de esa carpeta:
// apps/api/drizzle.config.ts
export default defineConfig({
schema: './src/db/schema/*',
out: './src/db/migrations',
dialect: 'postgresql',
dbCredentials: { url: process.env.DATABASE_URL! },
});Importante:
drizzle.config.tslee.env.local(víadotenv), así que apunta a la base de dev. La de producción se migra aparte (ver el último paso).
Pasos
1. Define la tabla en apps/api/src/db/schema/
Crea un archivo nuevo (o edita el del dominio que corresponda). Sigue el estilo de
apps/api/src/db/schema/tenants.ts: toda tabla con datos de cliente lleva id UUID,
tenantId con FK a tenants (onDelete: "cascade") y timestamps.
// apps/api/src/db/schema/widgets.ts
import {
pgTable,
uuid,
varchar,
jsonb,
timestamp,
} from "drizzle-orm/pg-core";
import { tenants } from "./tenants.js";
export const widgets = pgTable("widgets", {
id: uuid("id").defaultRandom().primaryKey(),
tenantId: uuid("tenant_id")
.references(() => tenants.id, { onDelete: "cascade" })
.notNull(),
name: varchar("name", { length: 255 }).notNull(),
color: varchar("color", { length: 50 }),
settings: jsonb("settings").default({}).notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at").defaultNow().notNull(),
});2. Regístrala en apps/api/src/db/index.ts
Drizzle Kit detecta el archivo por el glob, pero el cliente db en tiempo de ejecución
arma su schema importando cada archivo explícitamente. Si no lo agregas aquí, las
queries relacionales no conocerán la tabla:
// apps/api/src/db/index.ts
import * as widgetSchema from './schema/widgets.js';
export const db = drizzle(client, {
schema: {
// ...los demás...
...widgetSchema,
},
});3. Genera la migración
Desde la raíz del monorepo:
pnpm --filter @ixiclinic/api db:generateEsto crea un .sql nuevo en apps/api/src/db/migrations/ con el CREATE TABLE.
Nota:
db:generatepuede arrastrar drift (cambios no relacionados que ya existen en la BD pero no en el historial de migraciones). Revisa el.sqlgenerado y déjalo solo con lo nuevo de tu cambio; si entra drift,migrate.jspuede romper el arranque de producción. Ver Migraciones.
4. Aplica la migración en dev
pnpm --filter @ixiclinic/api db:migratePara iterar rápido en dev (sin generar archivo) puedes usar db:push, pero no lo uses
para cambios que vayan a producción — ahí siempre va por migración versionada.
5. Aplica el cambio en producción
⚠️ El esquema vive en DOS bases distintas:
- Dev: Neon Serverless PostgreSQL.
- Producción: PostgreSQL en Docker en el VPS (contenedor
ixiclinic-postgres).
Un cambio de esquema debe aplicarse a ambas. Tras desplegar el código, corre la
migración en prod siguiendo el procedimiento de
Migraciones y
Neon vs Docker. En prod la API corre las
migraciones con db:migrate:prod (node dist/migrate.js) sobre el contenedor de Postgres.
Scripts db:* (en apps/api)
| Script | Qué hace |
|---|---|
db:generate | Genera el .sql de migración desde el schema. |
db:migrate | Aplica migraciones pendientes (dev, Neon). |
db:migrate:prod | Aplica migraciones en prod (node dist/migrate.js). |
db:push | Empuja el schema directo a la BD — solo dev. |
db:studio | Abre Drizzle Studio. |
db:seed | Carga datos de prueba. |
Checklist
- Tabla definida en
apps/api/src/db/schema/<dominio>.ts(UUID +tenantIdFK + timestamps). - Registrada en el
schemadeapps/api/src/db/index.ts. pnpm --filter @ixiclinic/api db:generatey revisar el.sql(sin drift).pnpm --filter @ixiclinic/api db:migrateen dev (Neon).- Migración aplicada también en producción (Docker Postgres del VPS).
Más detalle en Migraciones · Drizzle.