Contenido del curso

Fundamentos y Primer CRUD

Base de Datos y Persistencia con TypeORM

Migraciones en TypeORM sin perder datos

Resumen

Versionar bases de datos es tan importante como versionar código en Git, y las migraciones de TypeORM son la herramienta para hacerlo de forma segura en proyectos NestJS. Aquí aprenderás cómo configurar el CLI, generar migraciones automáticas y ejecutarlas sin perder datos en producción.

¿Por qué usar migraciones en lugar de synchronize?

Cuando tu aplicación crece, también lo hace tu arquitectura de datos: agregas tablas, renombras columnas, mueves relaciones. Hacer esos cambios directo en producción es delicado porque ahí viven los datos reales de tus usuarios.

Dejar la opción synchronize: true en TypeORM es cómodo en desarrollo, pero peligroso fuera de él. Cada vez que cambias una entidad, TypeORM intenta ajustar la base de datos y puede borrar información o ejecutar cambios innecesarios [02:40].

Las migraciones resuelven esto al funcionar como commits sobre tu esquema:

  • Cada cambio queda registrado en un archivo con un timestamp único.
  • Sabes exactamente qué versión corre en local y cuál en producción.
  • Puedes revertir cambios con la operación revert.
  • Replicas el mismo esquema en cualquier ambiente.

¿Qué es una migración en TypeORM? Es un archivo versionado que describe un cambio en el esquema de la base de datos (crear tablas, alterar columnas, agregar índices) y que se ejecuta de forma controlada en cada ambiente.

¿Cómo configurar el data source para el CLI de TypeORM?

TypeORM trae dos piezas que conviven en tu proyecto NestJS: la conexión que usa la aplicación al correr en su puerto, y el CLI que gestiona las migraciones de forma aislada. Son dos cosas diferentes aunque apunten a la misma base de datos [05:50].

Dentro de src crea una carpeta database y un archivo orm-config.ts. Este archivo corre fuera del contexto de NestJS, así que no puedes usar ConfigService. En su lugar, lees variables de entorno con process.env apoyándote en dotenv, que ya viene con la dependencia de @nestjs/config.

La configuración debe incluir:

  • Las credenciales leídas desde process.env.
  • Una expresión regular que detecte todas las entidades terminadas en .entity.ts.
  • La ruta donde se guardarán las migraciones, normalmente src/database/migrations.
  • synchronize: false para que solo las migraciones modifiquen el esquema.

Es importante que este archivo viva dentro de src. Sacarlo de ahí rompe la estructura que NestJS espera para empaquetar y desplegar.

¿Cómo exponer el CLI desde los scripts de npm?

No necesitas instalar TypeORM ni ts-node de forma global porque ya están en el proyecto NestJS. Lo que haces es agregar un script base en package.json que ejecute el binario local con ts-node en modo CommonJS, apuntando con -d al archivo orm-config.ts.

El modo CommonJS es clave. Los proyectos NestJS resuelven módulos en CommonJS según el tsconfig.json, así que el paquete cargado debe coincidir.

A partir de ese script base se construyen tres comandos cortos:

  • migrations:generate para autogenerar una migración a partir de los cambios en las entidades.
  • migrations:run para aplicar las migraciones pendientes.
  • migrations:show para listar cuáles se ejecutaron y cuáles no.

¿Cómo generar y ejecutar la primera migración?

Antes de generar nada, conviene partir de una base de datos limpia si venías trabajando con synchronize: true. Con docker compose down -v bajas los contenedores y eliminas los volúmenes, dejando Postgres en cero [16:30].

Luego levantas con docker compose up -d y confirmas en tu cliente gráfico que el diagrama está vacío.

¿Cuál es la diferencia entre migration:create y migration:generate? Create crea un archivo vacío con el boilerplate para que escribas la migración a mano, útil cuando manejas SQL específico sobre millones de filas. Generate la autogenera comparando tus entidades con el estado actual de la base de datos.

Al correr npm run migrations:generate src/database/migrations/init-migration, TypeORM compara entidades contra la base de datos y produce un archivo con un identificador basado en el timestamp y dos métodos: up con las instrucciones SQL para aplicar el cambio, y down para revertirlo.

En el archivo generado verás cómo se crean las tablas profiles, categories y posts, los índices, y luego los ALTER TABLE que agregan las foreign keys y constraints.

¿Qué hace exactamente migration:run?

Al ejecutar npm run migrations:run, TypeORM lee qué migraciones están pendientes y aplica solo esas. Si vuelves a correrlo, te dirá que no hay nada por hacer.

La magia está en una tabla llamada migrations que TypeORM crea automáticamente en tu base de datos. Ahí registra cada migración aplicada, así sabe en qué versión está cada ambiente. En un deployment a producción, compara esa tabla con los archivos del repositorio y ejecuta solo las nuevas [25:10].

¿Necesito tener todo el esquema listo desde la primera migración? No. La primera migración refleja el estado inicial, y a partir de ahí generas una nueva migración por cada cambio: nueva tabla, columna renombrada o relación añadida. Cada cambio, su propia migración con nombre descriptivo.

¿Cómo se ve el flujo completo de trabajo?

Una vez configurado, el ciclo de trabajo se vuelve repetitivo y predecible:

  1. Modificas una entidad en tu código (agregas una columna, cambias un tipo).
  2. Ejecutas migrations:generate con un nombre que describa el cambio.
  3. Revisas el archivo generado para confirmar que el SQL es el esperado.
  4. Corres migrations:run en local para aplicarlo.
  5. Haces commit del archivo de migración junto con tu código.
  6. En producción, el mismo migrations:run aplica solo lo nuevo.

Antes de hacer deploy, migrations:show te da visibilidad: las migraciones con una X ya se ejecutaron, las que aparecen sin marca están pendientes. Si el comando falla con un error de conexión, sabes que tu data source tiene mal el host o las credenciales.

¿Has migrado un proyecto de synchronize a migraciones? Cuéntame en los comentarios cómo manejaste la primera migración sobre datos existentes.

      Migraciones en TypeORM sin perder datos