База даних Prisma’s 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, який масово змінює регістр у файлах схеми та може бути запущений знову, щоб нові поля не поверталися до своїх значень за замовчуванням
Який би варіант ви не обрали, прийміть рішення ще до першої міграції. Перейменування таблиць та стовпців пізніше означатиме необхідність створення міграцій, які впливають на існуючі дані, а кожен примітивний SQL-запит у кодбазі доведеться змінювати разом із ними.
Як 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 Walkthrough: A Watchlist API With Express 5, Prisma 7 and JWT — Опис завдання для роботи з повним стеком із часовими обмеженнями та його бекенд на Express, Prisma та JWT, із примітками щодо перевірки прав власності, каскадних дій та обробки помилок.
- Setting Up Prisma 7 with PostgreSQL in a TypeScript Node.js Project — Виправлення поширених помилок під час налаштування Prisma 7 у TypeScript, від проблем із рядками чи невизначеними URL до скарг щодо rootDir, а також підключення PostgreSQL за допомогою адаптера pg driver.