Несоответствие базы данных Shadow и номенклатуры в Prisma: руководство по эксплуатации
Почему принста migrate dev просит сбросить базу данных, как настроить безопасную теневую базу данных и как сопоставить правила регистрации в Prisma с форматом snake_case в Postgres.
При использовании Prisma с PostgreSQL постоянно возникают две проблемы: инструмент миграции продолжает предлагать удалить разработочную базу данных, а создаваемые им таблицы не соответствуют тем именам, которые выбрал бы администратор Postgres. Оба этих явления описаны в документации и не свидетельствуют о том, что Prisma непригоден для производства. В этом руководстве объясняется, что происходит в каждом случае, и приводится набор правил, помогающих сохранить ваши данные и схему без изменений.
Почему prisma migrate dev предлагает сбросить базу данных
Типичная ситуация выглядит следующим образом: кто-то запускает команду npx prisma migrate dev, она останавливается с ошибкой о том, что таблица или enum «уже существует», и кажется, что самый быстрый способ избавиться от этого сообщения — это использование команды prisma migrate reset. Эта команда удаляет все таблицы и перезапускает весь процесс миграции с нуля.
Для чего предназначена теневая база данных
Во время разработки Prisma Migrate использует вторую, временную базу данных под названием теневая база данных. Её единственная задача — обнаружение отклонений. При каждом запуске команды migrate dev Prisma создаёт чистую теневую базу данных, применяет к ней все файлы миграций, анализирует полученную схему и сравнивает её с реальной разработочной базой данных.
Когда эти схемы не совпадают, это означает, что в разработочной базе данных произошли изменения, не зафиксированные в истории миграций. Распространённые причины:
- команда
prisma db push, которая изменила таблицы без создания файла миграции - ручная правка, выполненная через SQL-клиент
- файл миграции, созданный коллегой, но так и не сохранённый
Prisma не может определить, какую версию данных считать актуальной, поэтому предлагает единственный безопасный автоматический вариант для разработочной базы данных: удаляет её и воссоздаёт на основе миграций.
Способ сбоя, который плохо задокументирован
Команды часто сталкиваются с другой проблемой, вызывающей похожие симптомы. В управляемых сервисах Postgres, таких как Neon или Supabase, пользователь базы данных, указанный в строке подключения, зачастую не имеет прав на создание и удаление баз данных по требованию. В результате Prisma не может создать временную резервную базу данных и выдаёт ошибку доступа.
Разработчики часто интерпретируют эту ошибку как «миграции сломаны» и, следуя советам из форумных тредов, запускают команду migrate reset, чтобы её устранить. Это опасно, поскольку именно команда reset надёжно уничтожает данные, если строка подключения указывает на реальный источник. В публичных обсуждениях на GitHub есть примеры именно таких ситуаций: ошибка в теневой базе данных, непланируемое выполнение команды reset в качестве «решения» и потеря таблиц на этапе реализации проекта.
Правила, обеспечивающие безопасность миграций
- Выделите для Prisma отдельную теневую базу данных. Установите значение
shadowDatabaseUrlна отдельную базу данных, в которой пользователи могут свободно создавать и удалять таблицы. Ни в коем случае не направляйте его на производственную базу или общую стадионную базу данных. В зависимости от версии Prisma эта настройка находится в конфигурации источника данных файла схемы или в файле настроек Prisma, поэтому ознакомьтесь с актуальной документацией, чтобы узнать, где требуется её указание. - Всегда рассматривайте команду
migrate resetкак разрушительную операцию. Если её предлагают в качестве первого шага при устранении неполадок, остановитесь и сначала проверьте учетные данные, права доступа и возможные отклонения в структуре данных. - Помните, что производственная среда отличается. Команда
prisma migrate deployприменяет только ожидающиеся миграции. Она никогда не создает теневую базу данных и не запрашивает сброс. Функция сброса является частью процесса разработки по замыслу создателей.
Модели в формате PascalCase против таблиц в формате snake_case
Вторая проблема связана с именованием. Язык схем Prisma рекомендует использовать имена моделей в формате PascalCase и имена полей в формате camelCase, что соответствует стандартам JavaScript и TypeScript. В мире Postgres обычно ожидается противоположное: идентификаторы в формате snake_case, часто с множественным числом в названиях таблиц.
При использовании стандартных настроек модель с именем User и полем firstName превращается в таблицу с таким же именем User и столбцом с именем firstName. Postgres это принимает, но идентификаторы с смешанными регистрами должны быть заключены в двойные кавычки в простом SQL, и такой формат кажется чуждим для администраторов БД, инструментов отчетности и любых сервисов, которые читают базу данных без использования Prisma.
Сопоставление имен с помощью @map и @@map
Prisma решает эту проблему с помощью двух атрибутов: @map переименовывает столбец отдельного поля, а @@map — таблицу, связанную с моделью. В вашем коде на TypeScript сохраняется обозначение user.firstName, в то время как в базе данных хранится users.first_name. Эта маппинг-система работает хорошо, но применяется вручную. У вас есть два варианта:
- вручную аннотировать каждое поле и модель, что трудоемко, но делает процесс полностью прозрачным и упрощает проверку
- использовать стороннюю утилиту
prisma-case-formatCLI, которая массово изменяет регистр в файлах схемы и может быть запущена снова, чтобы новые поля не возвращались к значениям по умолчанию
Какой бы вариант вы ни выбрали, принимайте решение до первой миграции. Переименование таблиц и столбцов позже означает необходимость создания миграций, влияющих на существующие данные, а каждый запрос к базе данных в кодовой базе тоже должен быть изменён.
Как Drizzle решает ту же проблему
Drizzle, самая известная альтернатива, ориентированная на TypeScript, предоставляет параметр casing, который преобразует имена в формате camelCase в коде в имена в формате snake_case в базе данных для всей схемы. Это редкий случай, когда обычная ситуация инвертируется. Prisma обычно считается более абстрактным инструментом, а Drizzle — ближе к SQL, однако подход Drizzle с кодом во главе упрощает работу с форматированием имен в масштабах всей схемы, тогда как отдельный язык схем Prisma оставляет возможность глобального форматирования в виде давнего запроса на добавление функции на момент написания текста.
Основные выводы
- Предупреждение о сбросе при выполнении команды
migrate devуказывает на отклонения или проблемы с правами доступа к теневой базе данных, а не на повреждённые миграции. - Настройте отдельную теневую базу данных для любого хостингового поставщика Postgres.
migrate reset в качестве универсального решения; производственные развертывания основаны на migrate deploy, который не может ничего сбрасывать.@map и @@map (вручную или с помощью prisma-case-format) до первой миграции, а не после.Если вы рассматриваете Prisma по сравнению с Drizzle в более широком контексте, наша статья raw SQL, Prisma и Drizzle рассматривает более общие аспекты выбора.
Связанные статьи
- Обзор MovieVault: API списка для просмотра с использованием Express 5, Prisma 7 и JWT — Подробное описание задания для полноценной разработки с указанием тайминга, а также информация о бэкенде на Express, Prisma и JWT, включая комментарии по проверке прав собственности, обработке каскадных ошибок и управлению ошибками.
- Настройка Prisma 7 с PostgreSQL в проекте на TypeScript и Node.js — Решение распространенных проблем при настройке Prisma 7 в TypeScript, от ошибок с строковыми или неопределенными URL до проблем с параметром rootDir, а также интеграция PostgreSQL с адаптером драйвера pg.