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 { defineMigration, defineModel, t } from '@jsorm/core';
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/core';
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/node';
await migrate(ormSource); // aplica todas las pendientes
await migrateDown(ormSource); // revierte el último batch

| Operación | Descripció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/core';
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/core';
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 o jsorm deploy --dry-run --strict en el pipeline antes del release.
  5. Trata migrate:down, db:fresh y db:rollback como comandos sensibles al entorno — mantenlos fuera de la automatización de producción.