Banco de Dados

16 nov, 2021

Tutorial de Migrations com Node.js e Sequelize

100 visualizações
Publicidade

Recentemente escrevi tutoriais de como usar Node.js com o ORM Sequelize usando os bancos MySQLPostgreSQL e SQLite. Na primeira parte sempre vemos como fazer o CRUD básico de Produtos e, na segunda parte, nosso foco maior foi no relacionamento das tabelas Produtos, Categorias e Fabricantes.

Tudo vai bem quando estamos em ambiente de dev, ainda criando nossa aplicação e modelando nossas tabelas. Eventualmente, se precisarmos, sempre podemos mandar o Sequelize forçar a recriação de toda a base usando a configuração de force no sync, como abaixo.

No entanto, temos três cenários em que você não vai querer usar o force e adotar abordagens mais profissionais:

  • quando você já tem dados na sua base que não quer que sejam perdidos;
  • quando já tem o seu projeto em produção;
  • quando você quer versionar as mudanças do seu banco de dados através de código;

Em todos os casos, o ideal é que você migre o schema do seu banco de dados para o novo schema, quer você tenha alterado tabelas já existentes ou criado tabelas novas.

A esse processo, damos o nome de migrations e esse é o assunto central do tutorial de hoje.

Se preferir, pode assistir ao vídeo abaixo, que possui o mesmo conteúdo.

Atenção: este tutorial serve perfeitamente bem para qualquer banco de dados que o Sequelize suporta. Apenas instale o driver apropriado e defina a connection string corretamente e tudo correrá bem.

 

Sequelize CLI

O Sequelize possui um utilitário de linha de comando chamado Sequelize CLI que auxilia em diversas atividades ligadas aos models da nossa aplicação, incluindo funcionalidades para nos ajudar com migrations. Como eu queria que a gente começasse sem ele, só estou apresentando agora.

Esse utilitário permite que você gere automaticamente models, que você crie as tabelas no banco e que você crie e execute migrations automaticamente. Não vou conseguir aqui apresentar todo o potencial dele, mas vale uma estudada na documentação oficial. Vamos usá-lo via linha de comando para criar nossa primeira migration.

Mas antes disso, precisamos fazer o setup do projeto.

Crie uma pasta e dentro dela rode o comando abaixo para inicializar o projeto.

Agora, instale as dependências que vamos precisar, o DotEnv e o Sequelize. O último pacote, mysql2, é porque vou usar MySQL. Caso escolha fazer este tutorial com outro banco, mude o pacote de acordo com seu banco (mssql, pg, sqlite3, etc).

Com as dependências instaladas, vamos criar o arquivo de configuração do nosso projeto, um .env na raiz da aplicação com as seguintes variáveis (coloque seus valores dentro).

Ali no DB_DIALECT você coloca mysql, mssql, postgres, etc; conforme o banco que estiver usando.

Agora, vamos inicializar nosso projeto com o Sequelize-CLI, rodando o comando abaixo.

Isso vai criar algumas pastas no projeto pra gente, sendo que apenas duas nos interessam neste tutorial específico, a config e a migrations.

Dentro da pasta config temos o arquivo de configuração do Sequelize-CLI, necessário para todos os demais comandos. Renomeie o arquivo config.json para config.js e altere o seu conteúdo para que fique como abaixo.

Repare que neste arquivo eu configurei três ambientes que poderiam ser diferentes, mas que deixei como iguais. Esses ambientes são chaveados automaticamente pelo Sequelize-CLI com base na variável NODE_ENV que é padrão em muitos servidores, sendo que o valor default é development.

Lembra que disse para você trocar o config.json para config.js? Pois é, para que a gente pudesse usar as variáveis de ambiente como fizemos. No entanto, esta alteração faz com que o Sequelize-CLI se perca um pouco pois ele espera um arquivo JSON. Para fazermos ele se achar novamente, crie um arquivo .sequelizerc na raiz do projeto com o seguinte conteúdo.

Isso diz ao Sequelize-CLI que ele deve procurar por um arquivo config.js para carregar suas configurações, ao invés do padrão config.json.

Com isso, estamos com tudo configurado para aprender sobre migrations.

Uma última dica: caso você não tenha criado a base de dados ainda, pode fazê-lo agora, usando o comando abaixo. É importante que a base exista, caso contrário os demais passos deste tutorial não vão funcionar.

Criando a Migration

Vamos imaginar primeiro um cenário bem simples, onde quero criar uma nova tabela no meu banco de dados, mas quero fazer isso em dev através de um script que depois possa ser usado pelos meus colegas de time ou mesmo na hora do deploy em produção.

Para criar uma migration, você usa o seguinte comando no console, que baixa e executa o Sequelize CLI com o comando de criação de migration.

Isso irá criar uma pasta migrations no seu projeto (se ela não existir) e dentro dela nosso primeiro arquivo de migration, idêntico ao abaixo.

Toda migration possui um up e um down, referente ao script de migration e o rollback da mesma. Isso permite que em caso de necessidade seja desfeita a migration, permitindo a gestão das alterações do banco de dados com muito mais detalhe.

Considere que em nossa pasta models temos um novo modelo, de Cliente, como abaixo (dando continuidade ao projeto do tutorial anterior).

Agora, para criar a migration de criação desta tabela, podemos modificar nosso arquivo de migration para isto.

Repare como coloquei o script de up praticamente idêntico ao do model de Cliente e embora pareça muito tentador carregar o model e passá-lo como parâmetro, isso não deve ser feito pois o model pode mudar ao longo do tempo e a migration jamais deve mudar. Conforme quisermos novas alterações no banco, devemos criar novas migrations, nunca alterar as já existentes.

Já o script de down é bem direto, apenas mando dropar a tabela que acabei de criar.

 

Executando a migration

Para executar esta migration, rode o comando abaixo que vai ir na pasta migrations e executar todos os scripts que estiverem lá e que ainda não foram executados, na ordem correta (do primeiro ao último).

O resultado será visto no console.

E no banco de dados, que deve conter agora uma tabela clientes.

Se você quiser testar se seu model Clientes está se comunicando corretamente com o banco de dados, o código abaixo pode ser utilizado em uma index.js na raiz da sua aplicação.

Se tudo der certo, um cliente deve ser cadastrado no seu banco de dados. Além disso, uma nova tabela deve ser criada, chamada SequelizeMeta. Se olhar o seu conteúdo, verá que ela mantém registro de quais migrations já foram executadas neste banco, o que será útil em execuções posteriores para que não ocorra repetição de execução.

Repare também que, diferente dos tutoriais anteriores, eu não usei o sync do Sequelize aqui, pois estou confiando de que meu model e minha tabela estão idênticos. Se não estiverem, provavelmente terei algum erro.

Trabalhar com migrations exige que o time entre em acordo de que não deve alterar um model sem criar uma nova migration, caso contrário terão problemas.

 

Alterando uma tabela

Imagine agora que algo mudou na regra e negócio da empresa e os clientes devem ter seus telefones registrados também, sendo que cada cliente vai ter apenas um telefone. Neste caso devemos criar uma nova migration específica para isso.

Assim, rode o comando abaixo para a nova migration.

Repare que cada vez que executamos o comando npx, o pacote é baixado e executado. Se for fazer isso com frequência e quiser ganhar tempo, pode instalar a dependência e rodar com ‘npm sequelize-cli’.

Agora no script de migration novo que foi criado, altere para incluir a nova coluna na tabela.

Aqui usamos a função addColumn do objeto queryInterface informando a tabela, a coluna e as configurações da coluna, usando a mesma notação que usamos para definição do model.

Aliás, adicione também esta nova coluna no seu model.

Para executar a migration e atualizar o seu banco, execute:

Lembra que falei da tabela SequelizeMeta que foi criada automaticamente? Ao mandarmos rodar o comando acima novamente, o Sequelize CLI vai ir nesta tabela para ver quais migrations ele precisa executar ainda e quais ele já executou e é por isso que, novamente, reforço que devemos sempre criar novas migrations e nunca alterar migrations já existentes.

Ao executar o comando no banco já existente, você notará que será impresso no console somente a migration de telefone, mas que se executar em um banco novo, vai executar as duas.

E por hoje é só!