Como aplicar migrations de uma API NestJS na VPS com um plano de retorno

Ilustração de servidor VPS executando migrations de banco de dados de uma API NestJS

Fala aí, beleza? Sua API NestJS já está no ar numa VPS que você mesmo administra, o banco tem dado de cliente de verdade e agora chegou uma migration nova pra aplicar 😅

O cenário aqui é esse: servidor próprio, PostgreSQL em uso, TypeORM ou Prisma cuidando do schema. O foco são duas coisas, a compatibilidade entre o schema novo e a versão da API que está rodando, e o fato de que backup, snapshot e migration:revert são ferramentas diferentes. Nenhuma delas é um botão de desfazer automático

O que você precisa antes de rodar migrations na VPS

Você precisa do mesmo que a documentação de deploy do NestJS pede pra qualquer deploy, mais um backup do banco feito na hora

Os pré-requisitos da doc são:

  • uma aplicação NestJS funcionando
  • acesso a uma plataforma ou servidor de deploy
  • variáveis de ambiente configuradas
  • os serviços que a API usa, como o banco de dados, já prontos
  • pelo menos uma versão LTS do Node.js no ambiente de deploy

A mesma documentação cita como produção possível tanto nuvens (AWS, Azure, Google Cloud) quanto um servidor dedicado administrado por você

Ou seja, VPS é caminho válido, só que a administração fica com você: atualizar o sistema, instalar Node.js, instalar as ferramentas do PostgreSQL e cuidar dos backups do banco

Pra esse cenário, a recomendação principal da Hora de Codar é a VPS da Hostinger (este artigo contém links de afiliado da Hostinger). Os motivos estão ligados à tarefa:

  • acesso root total, que você precisa pra instalar o Node.js e o pg_dump/pg_restore do jeito que o projeto pede
  • SSD NVMe e processadores AMD EPYC na base do servidor
  • backups semanais gratuitos e snapshot manual, que servem como camada extra de proteção do servidor inteiro

Se liga: essa camada extra não substitui o dump do banco. Os próximos passos mostram o porquê

Passo a passo: migrations do NestJS na VPS com backup e retorno

A ordem é: conferir compatibilidade, fazer o dump, (opcionalmente) tirar snapshot, compilar, aplicar, validar e, se der ruim, executar o retorno que você já tinha decidido

Passo 1: conferir a compatibilidade do schema

A versão da API que está no ar precisa continuar funcionando com o schema novo durante a janela de deploy

E o contrário também importa: se você precisar voltar o código, ele vai rodar contra um banco que já mudou?

Na prática, isso vira uma pergunta pra cada migration: ela só adiciona coisas (coluna nova, tabela nova) ou remove/renomeia algo que o código atual ainda lê? Remover e renomear são os casos que costumam travar o retorno

Isso é planejamento, não promessa de zero downtime. O objetivo é não depender de um retorno impossível

Erro comum deste passo: juntar na mesma migration a criação da coluna nova e a remoção da antiga. Aí a versão anterior da API quebra no minuto em que a migration roda

Passo 2: backup do banco com pg_dump

Antes de qualquer migration, gere um dump no formato custom (-Fc). Segundo a documentação do pg_dump, ele é um dos formatos mais flexíveis, já vem compactado por padrão e é restaurado com pg_restore

pg_dump -Fc nome_do_banco > antes_da_migration.dump

Use os dados de conexão do seu banco (usuário, host) do jeito que você já acessa ele na VPS

Esse arquivo é restaurado com o pg_restore, que permite restaurar de forma seletiva, por exemplo só parte do conteúdo em vez do banco todo. A própria doc do pg_dump avisa que restaurar um dump executa código arbitrário escolhido pelos superusuários da origem, então trate esse arquivo como algo sensível

Resultado esperado: um arquivo .dump no disco, gerado minutos antes do deploy

Erro comum deste passo: confiar só no backup automático da VPS. Segundo o suporte da Hostinger, esse backup é semanal por padrão (diário só como opção paga), então ele não foi feito no minuto anterior à migration, e tudo que entrou no banco depois dele fica de fora. A própria documentação do pg_dump diz que, fora casos simples, ele geralmente não é a escolha certa como backup regular de produção: mas aqui ele está sendo usado como dump pontual antes da migration, não como sua rotina de backup

Passo 3: snapshot da VPS (opcional)

Na Hostinger, o snapshot é criado manualmente na página Snapshots & Backups (na seção Backups & Monitoring > Snapshots & Backups), pelo botão Create Snapshot

Regras confirmadas no suporte da Hostinger:

  • só existe um snapshot por vez, e um novo sobrescreve o anterior
  • ele é apagado automaticamente depois de 1 dia
  • também é apagado se você reinstalar o sistema operacional ou restaurar de um backup

A própria Hostinger recomenda usar o snapshot como um checkpoint rápido e temporário antes de uma mudança, não como backup de longo prazo: por isso ele não substitui o dump do Passo 2

Erro comum deste passo: tratar o snapshot como rollback do banco. Tome cuidado! Restaurar o snapshot sobrescreve o conteúdo atual do servidor. Confira as condições de restauração no painel e no suporte antes de iniciar

Ou seja, ele é a saída pra quando o servidor inteiro deu problema, não pra desfazer uma coluna

Passo 4: build da aplicação e das migrations

Depois do build do NestJS, os arquivos compilados ficam em dist e o ponto de entrada é dist/main.js (ou dist/src/main.js, se você tiver arquivos .ts na raiz compilados pelo seu tsconfig)

No TypeORM tem um detalhe importante: migration:run e migration:revert só funcionam com arquivos .js. Se suas migrations são em TypeScript, elas precisam estar compiladas

A alternativa é rodar via ts-node:

npx typeorm-ts-node-commonjs migration:run -- -d path-to-datasource-config

Resultado esperado: dist/main.js existe e as migrations que você vai aplicar estão compiladas em .js (ou você escolheu de propósito o caminho com ts-node)

Erro comum deste passo: rodar migration:run apontando pra arquivos .ts. O comando não trabalha com eles

Passo 5: aplicar as migrations com TypeORM

O comando abaixo aplica as migrations que ainda não foram executadas no banco

typeorm migration:run -- -d path-to-datasource-config

E as transações? Segundo a documentação de setup de migrations do TypeORM, quem manda é a opção migrationsTransactionMode do DataSource:

  • all (padrão): todas as migrations numa única transação
  • each: uma transação por migration
  • none: sem transação

Pela CLI, dá pra usar a flag --transaction each

typeorm migration:run -- -d path-to-datasource-config --transaction each

Erro comum deste passo: assumir que o modo all protege qualquer comando. Se a migration tem algo que o seu banco não executa dentro de transação, confira isso na doc do banco antes e escolha o modo de propósito

Passo 6: aplicar as migrations com Prisma

Se a API usa Prisma ORM 7, o comando de produção é esse:

npx prisma@7 migrate deploy

Esse é o comando documentado pela Prisma para ambientes de produção, diferente dos comandos pensados pra desenvolvimento, como o migrate dev. Segundo a documentação do migrate deploy, ele não reseta o banco, não gera artefatos e não depende de shadow database

Atenção à versão: segundo o status oficial de versões do Prisma, até a data desta revisão, o Prisma ORM 8 ainda está em fase release candidate (sem versão final confirmada) e é o que npm install prisma instala hoje. A documentação dessa versão descreve um fluxo de migration baseado em prisma migration plan e prisma db migrate, no lugar dos comandos migrate usados no Prisma 7. Antes de colar esse passo no seu pipeline, confirme na documentação oficial do seu projeto se o comando equivalente de produção para a sua versão é realmente esse, e evite trocar o comando de produção por uma versão ainda em RC sem necessidade

Erro comum deste passo: usar no servidor os comandos pensados pra desenvolvimento. No Prisma 7, em produção fica no migrate deploy; se seu projeto estiver usando o Prisma ORM 8 (ainda em release candidate), a documentação dessa versão aponta para um fluxo diferente, com prisma migration plan e prisma db migrate: confirme o comando certo na doc oficial antes de aplicar em produção

Passo 7: subir a API e validar

Suba a versão nova a partir de dist/main.js, do jeito que seu processo de produção já faz. Se a sua API roda em container, vale ver como fica o fluxo de publicar NestJS com Docker Compose

Depois, valide em dois lugares:

  • tabela de controle de migrations: no TypeORM, o nome dela é definido por migrationsTableName (padrão migrations); no Prisma, é a _prisma_migrations. As migrations novas precisam aparecer lá como aplicadas
  • endpoints que dependem do schema novo: chame as rotas que leem ou gravam nas colunas/tabelas novas e confira se respondem sem erro

Se a tabela mostra a migration aplicada mas o endpoint falha, o problema provavelmente está no código ou na configuração, não no banco

Passo 8: executar o plano de retorno (se precisar)

O retorno é o que você decidiu ANTES do deploy, não algo que você improvisa com a API fora do ar

No TypeORM: o comando migration:revert desfaz apenas a última migration executada, rodando o método down definido no arquivo dela. Segundo a documentação de execução de migrations do TypeORM, pra desfazer mais de uma migration é preciso rodar o comando várias vezes, uma por uma, sempre desfazendo a mais recente ainda aplicada

typeorm migration:revert -- -d path-to-datasource-config

Antes de depender dele em produção, teste esse fluxo num banco de cópia e confira o estado real do schema após uma falha

No Prisma: não existe rollback automático, e o caminho muda segundo a versão e o estado da migration

  • No Prisma ORM 7, se a migration falhou no meio, a documentação de down migrations indica gerar uma down migration com migrate diff e aplicar com db execute, e depois marcar com migrate resolve --rolled-back. Se a migration rodou certinho e você quer desfazer, a doc manda voltar o schema.prisma pro estado anterior e gerar uma nova migration com migrate dev. Em nenhum dos dois casos a down migration desfaz mudanças de dados ou de código, só de schema
  • No Prisma ORM 8 (ainda em release candidate), não existe mais comando de down migration separado: voltar significa planejar uma nova migration que aponta pra um estado já visitado

Última saída, pra qualquer ORM: restaurar o dump do passo 2 com pg_restore

E o snapshot da VPS fica reservado pro caso de o servidor inteiro precisar voltar, lembrando que ele sobrescreve tudo

A migration falhou no meio do deploy: o que fazer

Primeiro: não rode a mesma migration de novo no susto. Descubra o que já foi aplicado

Sintoma: o deploy parou com parte do schema alterada, uma tabela criada e a coluna seguinte não, por exemplo

Causa: os passos que rodaram antes do erro continuam lá, principalmente quando nenhuma transação cobria aquela migration

Como resolver no Prisma

A documentação de patching e hotfix do Prisma 7 descreve essa ordem:

  1. marcar a migration como revertida
  2. desfazer manualmente os passos que já rodaram, ou tornar a migration idempotente (como CREATE TABLE ... IF NOT EXISTS)
  3. corrigir a causa do erro
  4. rodar npx prisma@7 migrate deploy de novo (confirme na documentação oficial o comando equivalente se seu projeto estiver no Prisma ORM 8, ainda em release candidate)

O passo 1 é feito assim:

npx prisma@7 migrate resolve --rolled-back "<nome_da_migration>"

Ele atualiza o registro na _prisma_migrations e permite aplicar a migration de novo. Até esse passo ser feito, a doc diz que não dá pra rodar novas migrations com migrate deploy

Tome cuidado: o resolve --rolled-back só funciona com migration que falhou. Ele não serve pra "desaplicar" uma migration que rodou certinho: usar nela gera erro

Como resolver no TypeORM

Revise qual migrationsTransactionMode estava ativo pra entender o que pode ter ficado pela metade

O comando migration:revert desfaz a última migration executada, conforme a documentação de reversão do TypeORM. Após uma falha, confira o estado real do schema e ensaie a recuperação num banco de cópia antes de executar comandos no banco de produção

Como prevenir

  • dump recente, feito logo antes do deploy
  • migrations pequenas e retrocompatíveis, uma mudança por vez
  • modo de transação escolhido de propósito, não por padrão
  • retorno ensaiado num banco de cópia antes de ir pra produção

Vídeo de apoio: VPS na prática com Evolution API e n8n

No vídeo abaixo, do canal Hora de Codar, tu vê na prática como configurar Evolution API e n8n numa VPS, pra automação com n8n e WhatsApp

Vídeo complementar do canal Hora de Codar, sobre um assunto relacionado.

Próximo passo: deixar o plano de retorno pronto antes do próximo deploy

Pega a próxima migration do seu projeto e escreve, antes de rodar, três linhas:

  • qual comando aplica
  • qual comando ou arquivo desfaz (o migration:revert, a down migration, o dump)
  • onde está o dump e qual o nome do arquivo

Depois, restaure esse dump com pg_restore num banco separado e confira se a API sobe apontando pra ele

Se funcionou, você tem um retorno de verdade, e não só um backup que ninguém nunca abriu haha

E se depois do deploy a API começar a pesar no servidor, antes de pensar em plano maior, vale ler sobre investigar o consumo de memória do NestJS

Agora, se você ainda vai escolher onde hospedar a API, dá pra conferir a VPS para este projeto com acesso root, NVMe e snapshot manual, sabendo que a administração do servidor fica com você

Até o próximo post! 😀

Perguntas frequentes

O backup automático da Hostinger substitui o pg_dump antes da migration?

Não. Segundo o suporte da Hostinger, o backup automático da VPS é semanal por padrão (diário é opção paga, com até 4 backups retidos). Isso significa que ele pode estar desatualizado em relação ao momento do deploy, dependendo de quando foi executado pela última vez, não apenas por um dia. O pg_dump feito minutos antes da migration é o que garante um ponto de retorno com os dados mais recentes do banco.

Quanto tempo o snapshot da VPS na Hostinger fica disponível?

Segundo o suporte da Hostinger, o snapshot é apagado automaticamente depois de 1 dia, além de ser removido se você reinstalar o sistema operacional ou restaurar de um backup. Por isso ele serve como checkpoint rápido antes de uma mudança, não como backup de longo prazo.

O comando npx prisma@7 migrate deploy ainda é o certo para produção?

Sim, para projetos no Prisma ORM 7, que é a versão estável e indicada pela documentação para rodar em produção (não reseta o banco, não gera artefatos e não depende de shadow database). O Prisma ORM 8 ainda está em fase release candidate até a data desta revisão, sem versão final confirmada. A documentação dessa versão descreve um fluxo baseado em prisma migration plan e prisma db migrate no lugar dos comandos de migration do Prisma 7; confirme na documentação oficial do seu projeto qual é o comando de produção correspondente antes de trocar esse comando.

O migration:revert do TypeORM desfaz quantas migrations por execução?

Segundo a documentação oficial do TypeORM, cada execução do migration:revert desfaz apenas a última migration aplicada, rodando o método down definido naquele arquivo. Para desfazer mais de uma migration, é preciso rodar o comando várias vezes, uma por vez, sempre revertendo a migration mais recente ainda registrada como aplicada.


Quer aprender mais sobre inteligência artificial e programação na prática? Conheça o canal Hora de Codar no YouTube e acompanhe os próximos vídeos.

Escrito por | Matheus Battisti

Matheus Battisti
Fundador da Hora de Codar

Programador apaixonado pelo mundo das tecnologias, sempre buscando em aprender e se aprofundar em linguagens, frameworks e o que mais for necessário para executar um bom trabalho. Agora tem uma nova missão que é de passar seu conhecimento adiante para formar novos programadores e especializar mais os que já são.

Formações

Formação Claude Code

Formação Claude Code

Domine Claude Code do absoluto zero até o avançado

  • 120 aulas
  • 4 projetos
  • 9h 48min

Blog | Mais populares