Ответ
В Node.js проектах миграции базы данных обычно выполняются до деплоя нового кода, как часть пайплайна CI/CD. Вот как я это организую:
Типичный процесс деплоя с миграциями:
-
Подготовительный этап (Pre-deployment)
// package.json { "scripts": { "migrate:up": "knex migrate:latest", "migrate:down": "knex migrate:rollback", "migrate:status": "knex migrate:status" } } -
Пайплайн в CI/CD (например, GitHub Actions)
# .github/workflows/deploy.yml jobs: deploy: steps: - name: Run database migrations run: npm run migrate:up env: DATABASE_URL: ${{ secrets.PRODUCTION_DB_URL }} - name: Verify migrations run: npm run migrate:status - name: Deploy application run: ./deploy.sh
Практический пример с Knex.js:
// migrations/20240101_create_users.js
exports.up = function(knex) {
return knex.schema.createTable('users', (table) => {
table.increments('id');
table.string('email').unique().notNullable();
table.string('password_hash').notNullable();
table.timestamps(true, true);
});
};
exports.down = function(knex) {
return knex.schema.dropTable('users');
};
// Скрипт для безопасного применения миграций
const runMigrations = async () => {
try {
console.log('Starting database migrations...');
// Бэкап перед миграциями (для production)
if (process.env.NODE_ENV === 'production') {
await backupDatabase();
}
// Применение миграций
const [batchNo, log] = await knex.migrate.latest();
console.log(`Migrations completed. Batch: ${batchNo}`);
log.forEach((migration) => {
console.log(`✓ ${migration}`);
});
return true;
} catch (error) {
console.error('Migration failed:', error);
// В production можно автоматически откатиться
if (process.env.NODE_ENV === 'production') {
await knex.migrate.rollback();
console.log('Rollback completed due to migration error');
}
throw error;
}
};
Лучшие практики, которые я применяю:
- Всегда делаю бэкап базы перед миграциями в production
- Использую транзакции для миграций, изменяющих данные
- Тестирую миграции на staging-окружении
- Реализую откатываемые стратегии деплоя (blue-green)
- Добавляю проверки целостности данных после миграций
Ответ 18+ 🔞
А, ну это же классика, ёпта! Слушай, с миграциями в Node.js проектах история такая же древняя, как и сама разработка, но до сих пор народ умудряется наступать на одни и те же грабли, ядрёна вошь.
Вот смотри, как у умных людей это устроено, чтобы не было потом волнения ебать на всю команду. Основной принцип — миграции обязательно гоняешь до того, как новый код попадёт на сервера. Иначе будет тебе хиросима и нигерсраки, когда твоё приложение начнёт искать колонку, которой нихуя нет.
Как это обычно в пайплайне выглядит:
-
Сначала подготовка, чувак. Всё по-взрослому, в
package.jsonскрипты забиваешь.// package.json { "scripts": { "migrate:up": "knex migrate:latest", "migrate:down": "knex migrate:rollback", // На всякий пожарный, если всё накрылось медным тазом "migrate:status": "knex migrate:status" // Чтобы проверить, не обосрался ли кто } } -
А потом в CI/CD (типа GitHub Actions) это всё автоматически и запускается.
# .github/workflows/deploy.yml jobs: deploy: steps: - name: Run database migrations run: npm run migrate:up env: DATABASE_URL: ${{ secrets.PRODUCTION_DB_URL }} # Ключи, конечно, в секретах, а то доверия ебать ноль к случайным строкам в коде - name: Verify migrations run: npm run migrate:status # Глянуть, что всё применилось - name: Deploy application run: ./deploy.sh # И только потом уже новый код выкатываешь
Вот тебе практический пример на Knex.js, чтобы было понятнее:
// migrations/20240101_create_users.js
exports.up = function(knex) {
return knex.schema.createTable('users', (table) => {
table.increments('id');
table.string('email').unique().notNullable();
table.string('password_hash').notNullable();
table.timestamps(true, true);
});
};
exports.down = function(knex) {
return knex.schema.dropTable('users'); // Функция отката — святое дело, никогда про неё не забывай
};
// А это уже скрипт для безопасного применения, чтобы не бздеть в production
const runMigrations = async () => {
try {
console.log('Starting database migrations...');
// Бэкап перед миграциями — это как презерватив: лучше иметь и не нуждаться, чем наоборот
if (process.env.NODE_ENV === 'production') {
await backupDatabase();
}
// Само применение миграций
const [batchNo, log] = await knex.migrate.latest();
console.log(`Migrations completed. Batch: ${batchNo}`);
log.forEach((migration) => {
console.log(`✓ ${migration}`);
});
return true;
} catch (error) {
console.error('Migration failed:', error);
// В продакшене если что-то пошло не так — немедленно откатывайся, не жди у моря погоды
if (process.env.NODE_ENV === 'production') {
await knex.migrate.rollback();
console.log('Rollback completed due to migration error');
}
throw error; // И падай с ошибкой, чтобы весь пайплайн остановился
}
};
А теперь, бля, лучшие практики, без которых ты просто распиздяй:
- Бэкап перед миграциями в production — это не обсуждается. Иначе сам от себя охуеешь, когда что-то пойдёт не так.
- Транзакции — твой лучший друг для миграций, которые данные меняют. Чтобы либо всё применилось, либо нихуя.
- Всё тестируй на staging-окружении. Выкатывать миграции сразу в прод — это игра в русскую рулетку с полным барабаном.
- Готовь стратегии отката (blue-green). Чтобы если новый код — говно, можно было мгновенно откатиться на старую версию.
- После миграций добавь проверки целостности. Убедись, что данные на месте и ни одна хитрая жопа из таблиц не сбежала.
Вот так-то, ебать копать. Соблюдаешь эти правила — спишь спокойно. Нет — готовься к ночным звонкам и крикам "всё упало!".