Cloudflare D1
Migrations
Schema
Deploy
SQLite

Migrations em D1: como versionar schema e o que acontece quando dá errado

O sistema de migrations do D1 é simples e funciona bem — até você precisar reverter uma migration que já rodou em produção e descobrir que não existe rollback nativo.

O sistema de migrations do D1 foi projetado para simplicidade, não para segurança. Arquivos .sql numerados em sequência, aplicados via wrangler d1 migrations apply, sem rollback. Comparado com ferramentas como Flyway, Liquibase ou até o sistema de migrations do Rails — que permitem migrations down e versionamento bidirecional — o D1 oferece um fluxo de mão única. Isso funciona bem quando a migration é bem testada e vai exatamente como planejado. Quando não vai, as opções de recuperação são limitadas e precisam ser preparadas antes do problema acontecer, não durante.

Como o sistema de migrations do D1 funciona

O wrangler d1 migrations create NOME cria um arquivo .sql numerado no diretório migrations/ do projeto. O número é sequencial e determina a ordem de aplicação — 0001_create_users.sql, 0002_add_status_column.sql e assim por diante. O wrangler rastreia quais migrations já foram aplicadas em uma tabela interna chamada D1_MIGRATIONS dentro do próprio banco.

wrangler d1 migrations apply NOME_DO_BANCO aplica todas as migrations pendentes em ordem. wrangler d1 migrations list --remote mostra o estado atual — quais foram aplicadas e quais estão pendentes. Esse fluxo é o suficiente para a maioria dos casos.

A limitação estrutural: não existe --rollback. Uma migration aplicada contra um banco D1 não tem desfazer automático. Se a migration 0015 introduziu um bug no schema e você precisa reverter, as opções são duas. A primeira é escrever a migration 0016 que desfaz manualmente o que a 0015 fez — possível para operações não destrutivas como adicionar uma coluna, mas impossível para operações que apagam dados, como DROP COLUMN ou DROP TABLE. A segunda é usar o Time Travel do D1 para restaurar o banco para um ponto anterior à migration, disponível para planos pagos com janela de 30 dias.

O problema de consistência de schema na borda

A replicação do D1 cria um risco de deploy que bancos tradicionais não têm: a migration pode estar no primário enquanto as réplicas ainda carregam o schema antigo. Um Worker que lê de uma réplica desatualizada vai executar queries contra um schema diferente do que espera.

O cenário concreto: você adiciona a coluna status à tabela orders com a migration 0020. A migration roda no primário. Simultaneamente, você faz deploy do Worker atualizado que faz SELECT status FROM orders. Um request que bate numa réplica que ainda não recebeu a migration vai falhar com um erro de coluna desconhecida — mesmo que o deploy tenha ido bem e o primário já tenha o schema correto.

O intervalo de propagação pode chegar a 60 segundos. A solução é separar o deploy da migration do deploy do código. O fluxo correto: aplicar a migration, aguardar explicitamente pelo menos 60 segundos, depois fazer deploy da nova versão do Worker. Esse wait precisa ser embutido no pipeline de CI/CD — o wrangler deploy não tem um flag de "aguardar propagação de schema", então você precisa construir esse delay manualmente. Um sleep 60 entre os dois passos do pipeline é suficiente para a grande maioria dos casos.

Playbook para migrations seguras em produção

Antes de aplicar qualquer migration em um banco de produção, exporte um dump completo: wrangler d1 export --remote --database NOME_DO_BANCO --output backup-$(date +%Y%m%d-%H%M).sql. Esse arquivo é o seu caminho de recuperação manual caso algo dê errado e o Time Travel não esteja disponível ou não seja suficiente.

Teste a migration em um banco de staging com dados de volume equivalente ao de produção. Uma migration que roda em 50ms contra uma tabela de 1.000 linhas pode levar 20 segundos contra uma tabela de 5 milhões de linhas — e durante esses 20 segundos, o banco pode estar indisponível para queries que dependem da tabela sendo alterada. O D1 não tem reindexação em background como o PostgreSQL com CREATE INDEX CONCURRENTLY.

Para mudanças de schema que precisam de zero downtime, use o padrão de deploy faseado. Para renomear uma coluna, o processo seguro em etapas independentes: primeiro, adicionar a nova coluna como nullable; segundo, fazer deploy do código que escreve nas duas colunas (antiga e nova); terceiro, um job de backfill que copia dados da coluna antiga para a nova nos registros existentes; quarto, atualizar o código para ler apenas da nova coluna; quinto, remover a coluna antiga em uma migration subsequente. Combinar todos esses passos em uma única migration cria uma janela de inconsistência e não permite rollback parcial.

Testando migrations localmente antes de tocar em produção

O wrangler dev --local cria um arquivo SQLite real em .wrangler/state/v3/d1/. Você pode aplicar migrations contra esse arquivo com wrangler d1 migrations apply NOME --local, inspecioná-lo diretamente com o cliente sqlite3, e rodar EXPLAIN QUERY PLAN contra as queries para verificar se os índices são usados corretamente antes de qualquer deployment.

O gap entre local e remoto é a replicação. O ambiente local é uma instância SQLite pura sem a camada de primário e réplicas — problemas de consistência eventual não vão aparecer em testes locais. Para cobrir esse gap, crie um banco D1 de staging separado no seu account Cloudflare, aplique as migrations ali primeiro, e valide o comportamento com dados que se aproximam do volume de produção.

O Time Travel é o plano B quando tudo falha. wrangler d1 time-travel restore --timestamp="2026-10-07T08:00:00Z" restaura o banco para o estado daquele ponto no tempo, dentro de uma janela de 30 dias em planos pagos. Isso recupera dados apagados por migrations destrutivas e reverte alterações de schema que não podem ser desfeitas por uma migration forward. A operação substitui o estado do banco inteiro — não existe restore seletivo de tabelas específicas. Se a migration corrompeu uma tabela mas outras tabelas tiveram atividade normal no intervalo, o restore vai reverter também essas outras tabelas para o estado anterior.

Leia também