Migrações de banco de dados são modificações feitas em um banco de dados. Essas modificações podem incluir a alteração do esquema de uma tabela, a atualização dos dados em um conjunto de registros, a inserção de dados ou a exclusão de uma faixa de registros.

As migrações de banco de dados geralmente são executadas antes que um aplicativo comece e não são executadas com sucesso mais de uma vez para o mesmo banco de dados. Ferramentas de migração de banco de dados salvam um histórico de migrações que foram executadas em um banco de dados para que possam ser rastreadas para fins futuros.

Neste artigo, você aprenderá como configurar e executar migrações de banco de dados em uma aplicação mínima de API em Node.js. Usaremos ts-migrate-mongoose e um script npm para criar uma migração e inserir dados em um banco de dados MongoDB. O ts-migrate-mongoose suporta a execução de scripts de migração a partir de código TypeScript, bem como de código CommonJS.

O ts-migrate-mongoose é um framework de migração para projetos Node.js que usam mongoose como o mapeador objeto-dados. Ele fornece um modelo para escrever scripts de migração. Também fornece uma configuração para executar os scripts programaticamente e a partir da CLI.

Índice

Como Configurar o Projeto

Para usar o ts-migrate-mongoose para migrações de banco de dados, você precisa ter o seguinte:

  1. Um projeto Node.js com mongoose instalado como uma dependência.

  2. Um banco de dados MongoDB conectado ao projeto.

  3. O MongoDB Compass (Opcional – para nos permitir visualizar as alterações no banco de dados).

Foi criado um repositório inicial que pode ser clonado de ts-migrate-mongoose-starter-repo para facilitar. Clone o repositório, preencha as variáveis de ambiente e inicie a aplicação executando o comando npm start.

Acesse http://localhost:8000 com um navegador ou um cliente de API como o Postman e o servidor retornará um texto “Olá!” para mostrar que a aplicação inicial está sendo executada conforme o esperado.

Como configurar o ts-migrate-mongoose para o projeto

Para configurar o ts-migrate-mongoose para o projeto, instale o ts-migrate-mongoose com este comando:

npm install ts-migrate-mongoose

O ts-migrate-mongoose permite a configuração com um arquivo JSON, um arquivo TypeScript, um arquivo .env ou via CLI. É aconselhável usar um arquivo .env porque o conteúdo da configuração pode conter uma senha de banco de dados e não é adequado que isso seja exposto ao público. Os arquivos .env geralmente são ocultados via arquivos .gitignore para que sejam mais seguros de usar. Este projeto usará um arquivo .env para a configuração do ts-migrate-mongoose.

O arquivo deve conter as seguintes chaves e seus valores:

  • MIGRATE_MONGO_URI – o URI do banco de dados Mongo. É o mesmo que o URL do banco de dados.

  • MIGRATE_MONGO_COLLECTION – o nome da coleção (ou tabela) na qual as migrações devem ser salvas. O valor padrão é migrations, que é o que é usado neste projeto. O ts-migrate-mongoose salva as migrações no MongoDB.

  • MIGRATE_MIGRATIONS_PATH – o caminho para a pasta onde os scripts de migração são armazenados e lidos. O valor padrão é ./migrations, que é o que é usado neste projeto.

Como Inserir Dados de Usuário com ts-migrate-mongoose

Nós fomos capazes de criar um projeto e conectá-lo com sucesso a um banco de dados Mongo. Neste ponto, queremos inserir dados de usuário no banco de dados. Precisamos:

  1. Criar uma coleção (ou tabela) de usuários

  2. Usar o ts-migrate-mongoose para criar um script de migração para inserir dados

  3. Usar o ts-migrate-mongoose para executar a migração e inserir os dados de usuário no banco de dados antes que a aplicação seja iniciada

1. Crie uma Coleção de usuários usando o Mongoose

Um esquema do Mongoose pode ser usado para criar uma coleção de usuários (ou tabela). Os documentos de usuário (ou registros) terão os seguintes campos (ou colunas): email, favouriteEmoji e yearOfBirth.

Para criar um esquema do Mongoose para a coleção de usuários, crie um arquivo user.model.js na raiz do projeto contendo o seguinte trecho de código:

const mongoose = require("mongoose");

const userSchema = new mongoose.Schema(
  {
    email: {
      type: String,
      lowercase: true,
      required: true,
    },
    favouriteEmoji: {
      type: String,
      required: true,
    },
    yearOfBirth: {
      type: Number,
      required: true,
    },
  },
  {
    timestamps: true,
  }
);

module.exports.UserModel = mongoose.model("User", userSchema);

2. Crie um Script de Migração com ts-migrate-mongoose

O ts-migrate-mongoose fornece comandos de CLI que podem ser usados para criar scripts de migração.

Executar npx migrate create <nome-do-script> na pasta raiz do projeto criará um script na pasta MIGRATE_MIGRATIONS_PATH (./migrations no nosso caso). <nome-do-script> é o nome que queremos que o arquivo do script de migração tenha quando for criado.

Para criar um script de migração para inserir dados de usuário, execute:

npx migrate create seed-users

O comando criará um arquivo na pasta ./migrations com um nome no formato – <timestamp>-seed-users.ts. O arquivo terá o seguinte conteúdo do trecho de código:

// Importe seus modelos aqui

export async function up (): Promise<void> {
  // Escreva a migração aqui
}

export async function down (): Promise<void> {
  // Escreva a migração aqui
}

A função up é usada para executar a migração. A função down é usada para reverter o que quer que a função up execute, se necessário. No nosso caso, estamos tentando adicionar usuários ao banco de dados. A função up conterá o código para adicionar usuários ao banco de dados e a função down conterá o código para deletar usuários criados na função up.

Se o banco de dados for inspecionado com o MongoDB Compass, a coleção de migrações terá um documento que se parece com isto:

{
  "_id": ObjectId("6744740465519c3bd9c1a7d1"),
  "name": "seed-users",
  "state": "down",
  "createdAt": 2024-11-25T12:56:36.316+00:00,
  "updatedAt": 2024-11-25T12:56:36.316+00:00,
  "__v": 0
}

O campo state do documento de migração é definido como down. Depois de ser executado com sucesso, ele muda para up.

Você pode atualizar o código em ./migrations/<timestamp>-seed-users.ts para o código no trecho abaixo:

require("dotenv").config() // carregar variáveis de ambiente
const db = require("../db.js")
const { UserModel } = require("../user.model.js");

const seedUsers = [
  { email: "[email protected]", favouriteEmoji: "🏃", yearOfBirth: 1997 },
  { email: "[email protected]", favouriteEmoji: "🍏", yearOfBirth: 1998 },
];

export async function up (): Promise<void> {
  await db.connect(process.env.MONGO_URI)
  await UserModel.create(seedUsers);}

export async function down (): Promise<void> {
  await db.connect(process.env.MONGO_URI)
  await UserModel.delete({
    email: {
      $in: seedUsers.map((u) => u.email),
    },
  });
}

3. Executar a Migração Antes do Início da Aplicação

O ts-migrate-mongoose nos fornece comandos de CLI para executar a função up e down dos scripts de migração.

Com npx migrate up <nome-do-script> podemos executar a função up de um script específico. Com npx migrate up podemos executar a função up de todos os scripts na pasta ./migrations com um state de down no banco de dados.

Para executar a migração antes que a aplicação seja iniciada, fazemos uso de scripts npm. Os scripts npm com um prefixo de pre serão executados antes de um script sem o prefixo pre. Por exemplo, se houver um script dev e um script predev, sempre que o script dev for executado com npm run dev, o script predev será executado automaticamente antes do script dev.

Vamos usar essa funcionalidade dos scripts npm para colocar o comando ts-migrate-mongoose em um script prestart para que a migração seja executada antes do script start.

Atualize o arquivo package.json para ter um script prestart que execute o comando ts-migrate-mongoose para executar a função up dos scripts de migração no projeto.

  "scripts": {
    "prestart": "npx migrate up",
    "start": "node index.js"
  },

Com essa configuração, quando npm run start é executado para iniciar a aplicação, o script prestart será executado para realizar a migração usando ts-migrate-mongoose e popular o banco de dados antes da aplicação ser iniciada.

Você deve ter algo semelhante ao trecho abaixo após executar npm run start:

Synchronizing database with file system migrations...
MongoDB connection successful
up: 1732543529744-seed-users.ts 
All migrations finished successfully

> [email protected] start
> node index.js

MongoDB connection successful                      
Server listening on port 8000

Confira o branch seed-users do repositório para ver o status atual da base de código neste ponto no artigo.

Como Construir um Endpoint de API para Obter Dados Populados

Podemos construir um endpoint de API para obter os dados de usuários populados em nosso banco de dados. No arquivo server.js, atualize o código para o mostrado no trecho abaixo:

const { UserModel } = require("./user.model.js")

module.exports = async function (req, res) {
  const users = await UserModel.find({}) // buscar todos os usuários no banco de dados

  res.writeHead(200, { "Content-Type": "application/json" });
  return res.end(JSON.stringify({ // retornar uma representação JSON dos dados dos usuários buscados
    users: users.map((u) => ({
      email: u.email,
      favouriteEmoji: u.favouriteEmoji,
      yearOfBirth: u.yearOfBirth,
      createdAt: u.createdAt
    }))
  }, null, 2));
};

Se iniciarmos a aplicação e visitarmos http://localhost:8000 usando o Postman ou um navegador, obtemos uma resposta JSON semelhante à mostrada abaixo:

{
  "users": [
    {
      "email": "[email protected]",
      "favouriteEmoji": "🏃",
      "yearOfBirth": 1997,
      "createdAt": "2024-11-25T14:18:55.416Z"
    },
    {
      "email": "[email protected]",
      "favouriteEmoji": "🍏",
      "yearOfBirth": 1998,
      "createdAt": "2024-11-25T14:18:55.416Z"
    }
  ]
}

Observe que se a aplicação for executada novamente, o script de migração não será executado novamente porque o estado da migração agora será up depois de ter sido executado com sucesso.

Confira o branch fetch-users do repositório para ver o status atual da base de código neste ponto do artigo.

Conclusão

As migrações são úteis ao construir aplicações e há necessidade de semear dados iniciais para testes, semear usuários administrativos, atualizar o esquema do banco de dados adicionando ou removendo colunas e atualizando os valores das colunas em muitos registros de uma vez.

O ts-migrate-mongoose pode ajudar a fornecer um framework para executar migrações para suas aplicações Node.js se você usar o Mongoose com o MongoDB.