Voltar para Artigos
Banco de Dados5 min de leitura

Migrations no TypeORM: Versionamento Seguro de Banco de Dados

Como usar o CLI do TypeORM para gerenciar migrations. Diferença entre migration:generate e migration:create, CI/CD para bancos de dados, reversibilidade (método down) e a regra de ouro do versionamento imutável.

12 de agosto de 2026

Executar um ALTER TABLE diretamente pelo DBeaver ou PgAdmin pode funcionar localmente, mas é o caminho garantido para o desastre no deploy: a aplicação espera uma coluna que não existe em produção e falha. Migrations tratam o banco de dados como código versionado: toda alteração de esquema (criar tabela, alterar coluna, adicionar índice) é descrita em um arquivo TypeScript executado sequencialmente.

Migration: Generate vs Create

O TypeORM oferece duas formas de criar migrations. A mais produtiva é a geração automática (migration:generate), onde o TypeORM compara suas Entidades (código) com o estado atual do banco e gera o SQL necessário automaticamente. A forma manual (migration:create) é para alterações que não derivam de uma entidade: seeds de dados, atualizações de dados existentes ou índices complexos que o gerador não sabe criar sozinho.

bash
# 1. Gera automaticamente baseada na diferença (Entidades vs Banco)
npx typeorm-ts-node-commonjs migration:generate src/infra/database/migrations/CreateUsersTable -d src/infra/database/data-source.ts

# 2. Cria uma migration vazia (para preencher manualmente com SQL customizado)
npx typeorm-ts-node-commonjs migration:create src/infra/database/migrations/SeedDefaultAdmin
1689230104821-CreateUsers.ts
import type { MigrationInterface, QueryRunner } from 'typeorm';

export class CreateUsers1689230104821 implements MigrationInterface {
  name = 'CreateUsers1689230104821';

  // Método UP: Avança a migration (o que deve ser feito)
  public async up(queryRunner: QueryRunner): Promise<void> {
    // Usando SQL puro é geralmente mais seguro para geradas automaticamente
    await queryRunner.query(`
      CREATE TABLE "users" (
        "id" uuid NOT NULL DEFAULT uuid_generate_v4(),
        "name" varchar NOT NULL,
        "email" varchar NOT NULL,
        "password" varchar NOT NULL,
        "created_at" TIMESTAMP NOT NULL DEFAULT now(),
        CONSTRAINT "UQ_97672ac88f789774dd47f708be3" UNIQUE ("email"),
        CONSTRAINT "PK_a3ffb1c0c8416b9fc6f907b7433" PRIMARY KEY ("id")
      )
    `);
  }

  // Método DOWN: Reverte a migration (como desfazer a alteração)
  // Essencial para o caso do deploy quebrar e precisarmos de rollback
  public async down(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.query(`DROP TABLE "users"`);
  }
}

Executando e Controlando Estado

O TypeORM cria uma tabela de metadados chamada migrations no seu banco que registra o nome (com timestamp) de cada migration executada. O timestamp no nome do arquivo é a chave de ordenamento: migrations são executadas sempre em ordem cronológica. A regra de ouro: nunca edite uma migration que já foi executada em produção. Se precisa corrigir, crie uma nova migration. Editar uma existente causa dessincronização entre o estado do banco e o registro da tabela migrations.

bash
# Roda todas as migrations pendentes (que ainda não estão na tabela migrations)
npx typeorm-ts-node-commonjs migration:run -d src/infra/database/data-source.ts

# Desfaz apenas a ÚLTIMA migration executada (roda o método down)
npx typeorm-ts-node-commonjs migration:revert -d src/infra/database/data-source.ts

A Regra de Ouro da Imutabilidade: Nunca edite o arquivo de uma migration que já foi commitada e compartilhada com a equipe (e principalmente as que já foram para produção). Se você esqueceu uma coluna, não edite a CreateUsers; rode um migration:generate criando a AddAvatarToUsers. Se você alterar um arquivo antigo, os bancos da equipe ficarão corrompidos em relação ao hash do arquivo.

Migrations em CI/CD

Em produção, as migrations devem rodar de forma automatizada no pipeline de Deploy, antes da nova versão do backend inicializar. Se a migration falhar (ex: erro de SQL), o deploy aborta e o servidor antigo continua rodando, garantindo zero-downtime.

deploy.yml
steps:
  - name: Instalar dependências
    run: npm ci

  - name: Build do TypeScript
    run: npm run build

  - name: Executar Migrations em Produção
    run: npm run typeorm migration:run
    env:
      DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }}

  - name: Reiniciar PM2/Servidor
    run: pm2 reload api

Conclusão

O uso de Migrations traz paz de espírito. Quando um dev júnior entrar na equipe, ele apenas rodará migration:run e terá a exata mesma estrutura de banco que o ambiente de Produção. A sincronização manual (synchronize: true do TypeORM) só deve ser usada em projetos rasos de laboratório — em produção, o controle rígido das migrations é inegociável.