Home / Articles / Prisma's Shadow Database and Naming Mismatch: An Operating Manual

This article is published in English.

Prisma's Shadow Database and Naming Mismatch: An Operating Manual

Why prisma migrate dev asks to reset your database, how to configure a safe shadow database, and how to map Prisma's casing to Postgres snake_case.

1028 words

Two complaints come up again and again when teams adopt Prisma with PostgreSQL: the migration tool keeps offering to wipe the development database, and the tables it creates do not look like anything a Postgres administrator would name. Both are documented behaviors, not signs that Prisma is unfit for production. This guide explains what is going on in each case and gives you a short set of rules that keep your data and your schema conventions intact.

Why prisma migrate dev offers to reset your database

The typical incident looks like this. Someone runs npx prisma migrate dev, the command stops with an error saying a table or enum "already exists", and the quickest way to make the message disappear seems to be prisma migrate reset. That command drops every table and replays the whole migration history from scratch.

What the shadow database is for

During development, Prisma Migrate uses a second, throwaway database called the shadow database. Its only job is drift detection. On every migrate dev run, Prisma creates a clean shadow database, applies all of your migration files to it, introspects the resulting schema, and compares that with the real development database.

When the two do not match, something changed the development database outside the migration history. Common causes are:

  • a prisma db push that altered tables without writing a migration file
  • a manual edit made through a SQL client
  • a migration file that a teammate created but never committed

Prisma cannot know which version of the truth you want to keep, so it proposes the only safe automatic option it has for a development database: drop it and rebuild it from the migrations.

The failure mode that is not well documented

What catches teams off guard is a different problem that produces similar symptoms. On managed Postgres services such as Neon or Supabase, the database user in your connection string often lacks permission to create and drop databases on demand. Prisma then cannot create its temporary shadow database and fails with a permission error.

Developers frequently read that error as "the migrations are broken" and, following advice from community threads, run migrate reset to clear it. That is dangerous, because reset is the one command that reliably destroys data if the connection string points somewhere real. Public GitHub discussions include exactly this story: a shadow-database error, an unplanned reset as the "fix", and tables lost in the middle of a project.

Rules that keep migrations safe

  • Give Prisma a dedicated shadow database. Set shadowDatabaseUrl to a separate database where your user may freely create and drop tables. Never point it at production or a shared staging database. Depending on your Prisma version, this setting lives in the datasource configuration of the schema file or in the Prisma config file, so check the current docs for where your version expects it.
  • Treat migrate reset as destructive, always. If it is suggested as a first troubleshooting step, stop and look at credentials, permissions and drift instead.
  • Understand that production is different. prisma migrate deploy applies pending migrations only. It never creates a shadow database and never prompts for a reset. The reset behavior belongs to the development workflow by design.

PascalCase models versus snake_case tables

The second friction point is naming. Prisma's schema language recommends PascalCase model names and camelCase field names, which matches idiomatic JavaScript and TypeScript. The Postgres world usually expects the opposite: snake_case identifiers, often with plural table names.

With default settings, a model called User with a field firstName becomes a table named User with a column named firstName. Postgres accepts this, but mixed-case identifiers must be double-quoted in raw SQL, and the result looks foreign to DBAs, reporting tools, and any service that reads the database without going through Prisma.

Mapping names with @map and @@map

Prisma solves this with two attributes: @map renames a single field's column, and @@map renames the table behind a model. Your TypeScript code keeps user.firstName, while the database stores users.first_name. The mapping works well, but it is not applied automatically. You have two options:

  • annotate every field and model by hand, which is tedious but completely explicit and easy to review
  • use the third-party prisma-case-format CLI, which rewrites the casing of a schema file in bulk and can be rerun to keep new fields from drifting back to the defaults

Whichever you choose, decide before the first migration. Renaming tables and columns later means writing migrations that touch existing data, and every raw SQL query in the codebase has to change with them.

How Drizzle handles the same problem

Drizzle, the most prominent TypeScript-first alternative, offers a casing setting that maps camelCase names in code to snake_case names in the database across the whole schema. This is a rare case where the usual story is reversed. Prisma is generally described as the more abstracted tool and Drizzle as the one closer to SQL, yet Drizzle's code-first schema made global casing easy, while Prisma's separate schema language has left a global option as a long-standing feature request at the time of writing.

Key takeaways

  • A reset prompt from migrate dev signals drift or a shadow-database permission problem, not corrupted migrations.
  • Configure an explicit, isolated shadow database for any hosted Postgres provider.
  • Never use migrate reset as a generic fix; production deployments rely on migrate deploy, which cannot reset anything.
  • Pick a naming strategy with @map and @@map (manually or with prisma-case-format) before your first migration, not after.

If you are weighing Prisma against Drizzle more broadly, our comparison of raw SQL, Prisma and Drizzle covers the wider trade-offs.