Roboweb/Блог
    Все статьи
    Разработчикам16 сентября 20268 мин чтения

    Prisma-миграции: как безопасно менять схему базы проекта

    Как перейти с prisma db push на миграции в выгруженном проекте Next.js + Prisma: базовая миграция, новые и обязательные поля, переименование и выкатка в прод.

    Prisma-миграции: как безопасно менять схему базы проекта

    Выгруженный из Roboweb проект — это Next.js, Prisma и PostgreSQL. После запуска в базе появляются живые данные: заявки, каталог, аккаунты посетителей. Пока база пустая, схему можно менять как угодно, но с появлением данных каждое изменение модели становится операцией над продом. Ниже — порядок работы с миграциями Prisma для такого проекта: как перейти с db push на историю миграций, добавить поле, сделать его обязательным, переименовать без потерь и выкатить изменения.

    Что проверить в выгрузке первым делом

    Откройте четыре места:

    • prisma/schema.prisma — модели, из которых построены таблицы проекта;
    • package.json — скрипт build выполняет prisma generate и next build, а скрипт db:push запускает prisma db push;
    • README — там описаны запуск и ограничения выгрузки;
    • папку prisma/migrations — есть ли она вообще.

    В текущей выгрузке миграций в комплекте нет: README прямо говорит, что схема разворачивается через prisma db push. Для первого запуска это удобно, для жизни с данными — нет. У db push нет истории, и одно и то же изменение не получится воспроизвести на локальной базе, стейджинге и проде. Общая структура выгрузки разобрана в статье что внутри экспорта.

    Дальше два сценария. Если папки prisma/migrations нет, а база уже работает с данными, начните со следующего раздела. Если миграции завёл кто-то до вас, выполните npx prisma migrate status, убедитесь, что все они применены, и переходите к добавлению полей.

    Ещё одна особенность: поля данных в моделях выгрузки объявлены как String? (типизированы только служебные id, createdAt и модели аккаунтов), а типы и связи намеренно оставлены разработчику. Ужесточать схему вы будете именно миграциями.

    Миграций нет, а данные уже есть: базовая миграция

    Базовая миграция (baseline) говорит Prisma: всё, что уже есть в базе, считать применённой начальной миграцией. Порядок действий:

    1. Сверьте схему в репозитории с реальной базой: npx prisma migrate diff --from-schema-datasource prisma/schema.prisma --to-schema-datamodel prisma/schema.prisma. Ответ «No difference detected» означает совпадение. Если разница есть, сначала выясните, что верно — схема или база.
    2. Создайте папку prisma/migrations/0_init и сгенерируйте в неё SQL от пустой базы до текущей схемы: npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script > prisma/migrations/0_init/migration.sql.
    3. Прочитайте получившийся SQL: это ровно то, что создало бы вашу базу с нуля.
    4. На каждой существующей базе — прод, стейджинг — отметьте миграцию как применённую, не выполняя её: npx prisma migrate resolve --applied 0_init.
    5. Закоммитьте папку prisma/migrations вместе со схемой.

    С этого момента db push для этих баз не используется. Скрипт db:push из package.json лучше удалить или переименовать, чтобы никто не запустил его по привычке. Локальную базу, созданную через db push, проще пересоздать: migrate dev заметит расхождение с историей и предложит сброс, и для локальной базы это нормально.

    Правило в одну строку: migrate dev — только на локальной базе, migrate deploy — на проде, db push — нигде, где есть данные, которые жалко потерять.

    Добавляем поле: migrate dev локально и коммит миграции

    Условный пример: в модель заявок, пусть она называется Lead, нужно добавить поле source — откуда пришёл клиент.

    1. Добавьте в схему строку source String? — пока необязательную.
    2. Выполните против локальной базы npx prisma migrate dev --name add_lead_source. Prisma создаст папку с меткой времени и файлом migration.sql, применит миграцию и перегенерирует клиент.
    3. Прочитайте SQL. ADD COLUMN — ожидаемо. DROP COLUMN или DROP TABLE там, где вы ничего не удаляли, — сигнал остановиться.
    4. Закоммитьте схему, папку миграции и код, который использует новое поле, одним изменением.

    Если SQL нужно поправить руками, добавьте флаг --create-only: миграция создаётся, но не применяется, и её можно отредактировать до запуска. Учтите также, что migrate dev использует временную теневую базу для проверки. Если у пользователя базы нет прав на создание баз, укажите отдельный shadowDatabaseUrl.

    Обязательное поле в заполненной таблице: умолчание или два шага

    Если сразу объявить поле обязательным, миграция на таблице с данными не пройдёт: существующим строкам нечего записать в новую колонку, и Prisma предупредит об этом. Вариантов два.

    Значение по умолчанию. Подходит, когда умолчание правдиво для старых записей: status String @default("new") — все существующие заявки получат статус new. Ставить умолчание, которое искажает данные, — плохая идея: пустой источник лучше выдуманного.

    Два шага. Надёжный путь для всего остального:

    1. Добавьте поле необязательным и выкатите.
    2. Заполните старые строки — отдельным UPDATE в миграции, созданной с --create-only, или разовым скриптом — и обновите код так, чтобы новые записи всегда получали значение.
    3. Проверьте запросом, что пустых значений не осталось, и только потом сделайте поле обязательным второй миграцией.

    Тот же подход нужен при ужесточении типов, без которого в выгрузке не обойтись. Допустим, цена в каталоге хранится как String?, а нужен Decimal. Не доверяйте автоматической смене типа: в сгенерированном SQL может оказаться пересоздание колонки или приведение, которое упадёт на первой строке вида «1 990 ₽». Безопаснее добавить новую колонку, перенести в неё очищенные значения, строки, которые не приводятся к числу, разобрать руками, переключить код и лишь затем удалить старую колонку.

    Переименование без потери данных

    Prisma не распознаёт переименование: если поменять имя поля в схеме, миграция удалит старую колонку и создаст новую, пустую. Безопасных способов два:

    • Переименовать только в коде. Дайте полю новое имя и добавьте @map со старым именем колонки, например contactPhone String? @map("phone"). Для модели то же делает @@map. Миграция не нужна, база не меняется.
    • Переименовать в базе. Создайте миграцию с --create-only и замените в SQL пару DROP COLUMN и ADD COLUMN одной командой ALTER TABLE "leads" RENAME COLUMN "phone" TO "contactPhone". Имя таблицы берите из @@map модели: в выгрузке у каждой модели оно совпадает с исходным именем таблицы проекта, а не с именем модели. Затем примените её через migrate dev.

    Учтите особенность выгрузки: маршрут app/api/data/[table] передаёт в Prisma имена полей формы как есть, а public/rw-export.js выводит значения по атрибуту data-rw-field. После переименования поля в модели обновите name и data-rw-field в разметке app/_site.ts, иначе отправка формы упадёт с ошибкой, а в каталоге и кабинете поле окажется пустым.

    Помните о моменте выкатки: пока новая версия приложения запускается, старая ещё может обращаться к колонке по прежнему имени. Если простоя быть не должно, выбирайте @map или переименовывайте в несколько шагов: новая колонка, запись в обе, перенос данных, переключение чтения, удаление старой.

    Выкатка в прод: migrate deploy, резервная копия, порядок действий

    1. Резервная копия непосредственно перед выкаткой: pg_dump -Fc -d "$DATABASE_URL" -f before-migration.dump — если в строке подключения есть параметр schema, для pg_dump его уберите: Prisma его понимает, pg_dump нет. Хотя бы раз заранее убедитесь, что копия восстанавливается через pg_restore на тестовую базу.
    2. Статус. Команда npx prisma migrate status на проде покажет, какие миграции ещё не применены.
    3. Применение. Команда npx prisma migrate deploy применяет только новые миграции из папки, не сбрасывает базу и не создаёт новых миграций. В скриптах выгрузки её нет — добавьте отдельный шаг в процесс выкатки.
    4. Запуск новой версии приложения — только после успешного применения миграций.
    5. Проверка ключевых сценариев: отправка формы, каталог, вход в кабинет, оформление заказа.

    Если миграция упала посередине, Prisma отметит её как неудачную и не станет применять следующие. Исправьте базу вручную или восстановите копию, затем отметьте миграцию командой npx prisma migrate resolve --rolled-back с её именем и выкатите исправленную версию. Где разместить проект с PostgreSQL и как устроить выкатку, разобрано в статье где разместить проект на Next.js и PostgreSQL.

    Частые ошибки

    • db push в проде. Истории нет, а при конфликте команда предложит флаг --accept-data-loss, который делает ровно то, что написано.
    • Правка уже применённой миграции. Prisma хранит контрольные суммы: migrate dev заметит изменение и предложит сбросить базу, а истории на разных окружениях разойдутся. Исправление — всегда новая миграция поверх.
    • migrate dev или migrate reset против прода. Обе команды рассчитаны на разработку и могут удалить данные.
    • Незакоммиченная папка миграций. Локально всё работает, а на проде migrate deploy её не увидит, и код обратится к колонке, которой нет.
    • Смешивание db push и миграций в одной базе. Появляется расхождение, которое потом разбирают вручную.
    • Повторная выгрузка поверх ручных правок. Новая выгрузка из Roboweb ничего не знает о ваших миграциях: сравните schema.prisma через git diff и оформите разницу отдельной миграцией, а не копируйте файлы поверх.

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

    Понравилась статья?

    Поделитесь с коллегами и друзьями

    Telegram

    Готовы запустить свой продукт?

    Опишите идею — ИИ соберёт фуллстек с бэкендом, а код останется вашим активом

    Создать проект бесплатно