Миграции баз данных — это изменения, вносимые в базу данных. Эти изменения могут включать в себя изменение схемы таблицы, обновление данных в наборе записей, заполнение данных или удаление диапазона записей.

Миграции баз данных, как правило, выполняются перед запуском приложения и не выполняются успешно более одного раза для одной и той же базы данных. Инструменты миграции баз данных сохраняют историю миграций, которые были выполнены в базе данных, чтобы их можно было отслеживать для будущих целей.

В этой статье вы узнаете, как настроить и выполнить миграции баз данных в минимальном приложении API на Node.js. Мы будем использовать ts-migrate-mongoose и скрипт npm для создания миграции и заполнения данных в базе данных MongoDB. ts-migrate-mongoose поддерживает выполнение миграционных скриптов из кода TypeScript, а также кода CommonJS.

ts-migrate-mongoose — это фреймворк миграций для проектов на Node.js, которые используют mongoose в качестве сопоставителя объект-данные. Он предоставляет шаблон для написания миграционных скриптов. Также он предоставляет конфигурацию для программного запуска скриптов и из командной строки.

Содержание

Как настроить проект

Для использования ts-migrate-mongoose для миграций базы данных вам понадобится следующее:

  1. Проект Node.js с установленным mongoose в качестве зависимости.

  2. База данных MongoDB, подключенная к проекту.

  3. MongoDB Compass (необязательно – для того, чтобы мы могли видеть изменения в базе данных).

Создан репозиторий-стартер, который можно клонировать из ts-migrate-mongoose-starter-repo для удобства. Клонируйте репозиторий, заполните переменные окружения и запустите приложение, выполнив команду npm start.

Посетите http://localhost:8000 с помощью браузера или клиента API, такого как Postman, и сервер вернет текст “Hello there!”, чтобы показать, что стартовое приложение работает как ожидалось.

Как настроить ts-migrate-mongoose для проекта

Чтобы настроить ts-migrate-mongoose для проекта, установите ts-migrate-mongoose с помощью этой команды:

npm install ts-migrate-mongoose

ts-migrate-mongoose позволяет настраивать с помощью JSON файла, TypeScript файла, файла .env или через CLI. Рекомендуется использовать файл .env, так как содержимое конфигурации может содержать пароль от базы данных, и неуместно, чтобы это было доступно публично. Файлы .env обычно скрыты через файлы .gitignore, что делает их более безопасными для использования. Этот проект будет использовать файл .env для конфигурации ts-migrate-mongoose.

Файл должен содержать следующие ключи и их значения:

  • MIGRATE_MONGO_URI – URI базы данных Mongo. Это то же самое, что и URL базы данных.

  • MIGRATE_MONGO_COLLECTION – имя коллекции (или таблицы), в которой должны быть сохранены миграции. Значение по умолчанию – migrations, что используется в этом проекте. ts-migrate-mongoose сохраняет миграции в MongoDB.

  • MIGRATE_MIGRATIONS_PATH – путь к папке для хранения и чтения скриптов миграции. Значение по умолчанию – ./migrations, что используется в этом проекте.

Как заполнить данные пользователей с помощью ts-migrate-mongoose

Мы смогли создать проект и успешно подключить его к базе данных Mongo. На этом этапе мы хотим заполнить данные пользователей в базу данных. Нам нужно:

  1. Создать коллекцию пользователей

  2. Использовать ts-migrate-mongoose для создания скрипта миграции для заполнения данных

  3. Использовать ts-migrate-mongoose для запуска миграции для заполнения данных пользователей в базу данных перед запуском приложения

1. Создайте коллекцию пользователей с помощью Mongoose

Схема Mongoose может быть использована для создания коллекции пользователей (или таблицы). Документы пользователей (или записи) будут иметь следующие поля (или столбцы): email, favouriteEmoji и yearOfBirth.

Чтобы создать схему Mongoose для коллекции пользователей, создайте файл user.model.js в корне проекта, содержащий следующий фрагмент кода:

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. Создайте скрипт миграции с помощью ts-migrate-mongoose

ts-migrate-mongoose предоставляет интерфейс командной строки (CLI), который можно использовать для создания скриптов миграции.

Запуск npx migrate create <имя-скрипта> в корневой папке проекта создаст скрипт в папке MIGRATE_MIGRATIONS_PATH (в нашем случае ./migrations). <имя-скрипта> – это имя, которое мы хотим, чтобы у файла скрипта миграции после его создания.

Для создания скрипта миграции для заполнения данных пользователей, выполните:

npx migrate create seed-users

Команда создаст файл в папке ./migrations с именем в формате – <timestamp>-seed-users.ts. Файл будет содержать следующее содержимое фрагмента кода:

// Импортируйте ваши модели здесь

export async function up (): Promise<void> {
  // Напишите миграцию здесь
}

export async function down (): Promise<void> {
  // Напишите миграцию здесь
}

Функция up используется для запуска миграции. Функция down используется для отмены действий, выполненных функцией up, если это необходимо. В нашем случае мы пытаемся добавить пользователей в базу данных. Функция up будет содержать код для добавления пользователей в базу данных, а функция down будет содержать код для удаления пользователей, созданных функцией up.

Если база данных проверяется с помощью MongoDB Compass, в коллекции миграций будет документ, который выглядит следующим образом:

{
  "_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
}

Поле state документа миграции установлено в down. После успешного выполнения оно изменится на up.

Вы можете обновить код в ./migrations/<timestamp>-seed-users.ts на приведенный ниже:

require("dotenv").config() // загрузить переменные среды
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. Запуск миграции перед запуском приложения

ts-migrate-mongoose предоставляет нам команды CLI для выполнения функций up и down скриптов миграции.

С помощью npx migrate up <name-of-script> мы можем выполнить функцию up определенного скрипта. С помощью npx migrate up мы можем выполнить функцию up всех скриптов в папке ./migrations со значением state равным down в базе данных.

Для запуска миграции перед запуском приложения мы используем npm-скрипты. npm-скрипты с префиксом pre будут выполняться перед скриптом без префикса pre. Например, если есть скрипт dev и скрипт predev, то при запуске скрипта dev с помощью npm run dev, скрипт predev будет автоматически запускаться перед выполнением скрипта dev.

Мы будем использовать эту функцию npm-скриптов, чтобы поместить команду ts-migrate-mongoose в скрипт prestart, чтобы миграция выполнялась перед запуском скрипта start.

Обновите файл package.json, чтобы добавить скрипт prestart, который будет запускать команду ts-migrate-mongoose для выполнения функции up миграционных скриптов в проекте.

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

С этой настройкой, при выполнении команды npm run start для запуска приложения будет выполняться скрипт prestart для выполнения миграции с использованием ts-migrate-mongoose и заполнения базы данных перед запуском приложения.

После выполнения команды 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

Ознакомьтесь с веткой seed-users репозитория, чтобы увидеть текущее состояние кодовой базы на данном этапе в статье.

Как создать конечную точку API для получения данных о пользователе

Мы можем создать конечную точку API для получения данных о засеянных пользователях в нашей базе данных. В файле server.js обновите код на тот, что приведен в отрывке ниже:

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

module.exports = async function (req, res) {
  const users = await UserModel.find({}) // получить всех пользователей в базе данных

  res.writeHead(200, { "Content-Type": "application/json" });
  return res.end(JSON.stringify({ // вернуть JSON-представление данных о полученных пользователях
    users: users.map((u) => ({
      email: u.email,
      favouriteEmoji: u.favouriteEmoji,
      yearOfBirth: u.yearOfBirth,
      createdAt: u.createdAt
    }))
  }, null, 2));
};

Если мы запустим приложение и посетим http://localhost:8000 с помощью Postman или браузера, мы получим JSON-ответ, подобный приведенному ниже:

{
  "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"
    }
  ]
}

Обратите внимание, что если приложение будет запущено снова, скрипт миграции больше не будет запущен, потому что state миграции теперь будет up, после успешного выполнения.

Проверьте ветку fetch-users в репозитории, чтобы увидеть текущий статус кодовой базы на этом этапе в статье.

Заключение

Миграции полезны при создании приложений и есть необходимость в заполнении начальных данных для тестирования, заполнении административных пользователей, обновлении схемы базы данных путем добавления или удаления колонок и обновлении значений колонок во многих записях одновременно.

ts-migrate-mongoose может помочь создать фреймворк для выполнения миграций для ваших приложений Node.js, если вы используете Mongoose с MongoDB.