Выгруженный из 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: всё, что уже есть в базе, считать применённой начальной миграцией. Порядок действий:
- Сверьте схему в репозитории с реальной базой: npx prisma migrate diff --from-schema-datasource prisma/schema.prisma --to-schema-datamodel prisma/schema.prisma. Ответ «No difference detected» означает совпадение. Если разница есть, сначала выясните, что верно — схема или база.
- Создайте папку prisma/migrations/0_init и сгенерируйте в неё SQL от пустой базы до текущей схемы: npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script > prisma/migrations/0_init/migration.sql.
- Прочитайте получившийся SQL: это ровно то, что создало бы вашу базу с нуля.
- На каждой существующей базе — прод, стейджинг — отметьте миграцию как применённую, не выполняя её: npx prisma migrate resolve --applied 0_init.
- Закоммитьте папку prisma/migrations вместе со схемой.
С этого момента db push для этих баз не используется. Скрипт db:push из package.json лучше удалить или переименовать, чтобы никто не запустил его по привычке. Локальную базу, созданную через db push, проще пересоздать: migrate dev заметит расхождение с историей и предложит сброс, и для локальной базы это нормально.
Правило в одну строку: migrate dev — только на локальной базе, migrate deploy — на проде, db push — нигде, где есть данные, которые жалко потерять.
Добавляем поле: migrate dev локально и коммит миграции
Условный пример: в модель заявок, пусть она называется Lead, нужно добавить поле source — откуда пришёл клиент.
- Добавьте в схему строку source String? — пока необязательную.
- Выполните против локальной базы npx prisma migrate dev --name add_lead_source. Prisma создаст папку с меткой времени и файлом migration.sql, применит миграцию и перегенерирует клиент.
- Прочитайте SQL. ADD COLUMN — ожидаемо. DROP COLUMN или DROP TABLE там, где вы ничего не удаляли, — сигнал остановиться.
- Закоммитьте схему, папку миграции и код, который использует новое поле, одним изменением.
Если SQL нужно поправить руками, добавьте флаг --create-only: миграция создаётся, но не применяется, и её можно отредактировать до запуска. Учтите также, что migrate dev использует временную теневую базу для проверки. Если у пользователя базы нет прав на создание баз, укажите отдельный shadowDatabaseUrl.
Обязательное поле в заполненной таблице: умолчание или два шага
Если сразу объявить поле обязательным, миграция на таблице с данными не пройдёт: существующим строкам нечего записать в новую колонку, и Prisma предупредит об этом. Вариантов два.
Значение по умолчанию. Подходит, когда умолчание правдиво для старых записей: status String @default("new") — все существующие заявки получат статус new. Ставить умолчание, которое искажает данные, — плохая идея: пустой источник лучше выдуманного.
Два шага. Надёжный путь для всего остального:
- Добавьте поле необязательным и выкатите.
- Заполните старые строки — отдельным UPDATE в миграции, созданной с --create-only, или разовым скриптом — и обновите код так, чтобы новые записи всегда получали значение.
- Проверьте запросом, что пустых значений не осталось, и только потом сделайте поле обязательным второй миграцией.
Тот же подход нужен при ужесточении типов, без которого в выгрузке не обойтись. Допустим, цена в каталоге хранится как 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, резервная копия, порядок действий
- Резервная копия непосредственно перед выкаткой: pg_dump -Fc -d "$DATABASE_URL" -f before-migration.dump — если в строке подключения есть параметр schema, для pg_dump его уберите: Prisma его понимает, pg_dump нет. Хотя бы раз заранее убедитесь, что копия восстанавливается через pg_restore на тестовую базу.
- Статус. Команда npx prisma migrate status на проде покажет, какие миграции ещё не применены.
- Применение. Команда npx prisma migrate deploy применяет только новые миграции из папки, не сбрасывает базу и не создаёт новых миграций. В скриптах выгрузки её нет — добавьте отдельный шаг в процесс выкатки.
- Запуск новой версии приложения — только после успешного применения миграций.
- Проверка ключевых сценариев: отправка формы, каталог, вход в кабинет, оформление заказа.
Если миграция упала посередине, 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; подробнее об этом в статье безопасность данных бизнеса. Практический шаг на сегодня: сделайте базовую миграцию, пока ручных изменений в проекте мало, — через месяц правок разбирать расхождения будет заметно сложнее.


