Ir al contenido

Migraciones

Conéctate a una base de datos existente y usa modelos de inmediato — adopta el runtime de migraciones solo cuando necesites gestión del ciclo de vida del schema.

Cada migración es un objeto con name, up, y opcionalmente down. Los callbacks reciben t — el builder de migraciones:

import { defineModel, defineMigration, t } from 'jsorm';
const Role = defineModel('roles', {
id: t.number().primary(),
name: t.string().unique(),
});
const User = defineModel('users', {
id: t.number().primary(),
name: t.string(),
email: t.string().optional(),
active: t.boolean().default(true).index(),
role: t.belongsTo(Role),
});
const initRolesAndUsers = defineMigration({
name: 'init-roles-and-users',
up: (t) => [
t.createTable(Role),
t.createTable(User),
],
down: (t) => [
t.dropTable('users'),
t.dropTable('roles'),
],
});
const addUserTimezone = defineMigration({
name: 'add-user-timezone',
up: (t) => [
t.addColumn('users', 'timezone', t.string().optional()),
],
down: (t) => [
t.dropColumn('users', 'timezone', { ifExists: true }),
],
});

Un migration source agrupa un adaptador con una lista ordenada de migraciones:

import { defineMigrationSource } from 'jsorm';
import { pgAdapter } from 'jsorm-pg';
const ormSource = defineMigrationSource({
adapter: pgAdapter({
name: 'main',
connectionString: process.env.DATABASE_URL!,
}),
migrations: [initRolesAndUsers, addUserTimezone],
migrationTable: 'jsorm_migrations',
});
import { migrate, migrateDown } from 'jsorm';
await migrate(ormSource); // aplica todas las pendientes
await migrateDown(ormSource); // revierte el último batch
OperaciónDescripción
t.createTable(Model)Crea tabla desde definición de modelo
t.dropTable(tableName)Elimina una tabla
t.addColumn(table, name, field)Agrega una columna
t.dropColumn(table, name, opts?)Elimina una columna
t.renameColumn(table, from, to)Renombra una columna
t.createIndex(table, columns)Agrega un índice
t.dropIndex(table, name)Elimina un índice

Para DDL complejo que el builder no cubre, usa rawSql():

import { defineMigration, rawSql } from 'jsorm';
const addFullTextIndex = defineMigration({
name: 'add-fulltext-index',
up: [
rawSql(
"CREATE INDEX users_name_fts ON users USING GIN (to_tsvector('english', name))",
'DROP INDEX IF EXISTS users_name_fts',
),
],
down: [
rawSql(
'DROP INDEX IF EXISTS users_name_fts',
"CREATE INDEX users_name_fts ON users USING GIN (to_tsvector('english', name))",
),
],
});

Cuando jsorm.config.ts tiene migrationSources + defaults.migrationSource configurados, los comandos CLI no necesitan argumentos:

Ventana de terminal
jsorm migrate:status # verificar pendientes
jsorm migrate # aplicar todas las pendientes
jsorm migrate:up # aplicar la siguiente
jsorm migrate:down # revertir último batch (protegido en prod)

Pasa el archivo JS compilado y el nombre exportado cuando no uses config-first:

Ventana de terminal
jsorm migrate ./dist/schema.js ormSource
jsorm migrate:up ./dist/schema.js ormSource
jsorm migrate:down ./dist/schema.js ormSource
jsorm migrate:status ./dist/schema.js ormSource

jsorm registra las migraciones aplicadas en jsorm_migrations (configurable vía migrationTable):

-- auto-gestionada, no modificar manualmente
id SERIAL PRIMARY KEY
name TEXT NOT NULL UNIQUE
batch INTEGER NOT NULL
applied_at TIMESTAMP NOT NULL DEFAULT NOW()

Genera archivos de migración revisados haciendo diff de los modelos actuales contra un snapshot almacenado:

// jsorm.config.ts
import { defineMigrationGenerateSource } from 'jsorm';
export const generateSource = defineMigrationGenerateSource({
models: [Role, User],
snapshotPath: '.migrations/schema.json',
});

Luego ejecuta:

Ventana de terminal
jsorm migrate:generate

El generador clasifica cada cambio como safe, review_required o dangerous, emite un archivo de migración con timestamp, y registra las tablas many-to-many pivot detectadas.

  1. Mantén las migraciones pequeñas — un concepto por migración.
  2. Escribe siempre down cuando un paso no sea automáticamente reversible.
  3. Nunca modifiques una migración ya aplicada.
  4. Ejecuta migrate:status en el deploy para confirmar que todas se aplicaron.
  5. Trata migrate:down, db:fresh y db:rollback como comandos sensibles al entorno — mantenlos fuera de la automatización de producción.