JavaScriptTypeScriptSQL Prisma Database DesignBackend DevelopmentBackend Engineering 6 min read

Prisma ORM 8.0.0-rc.21 Adds MySQL Support and Significant Breaking Changes

The latest Prisma ORM release introduces MySQL support and fundamental changes to error handling, PostgreSQL temporal types, and schema field presets.

Prisma ORM 8.0.0-rc.21, published on 30 October, adds enum value renames and deletes on Postgres and SQLite. It also ships the first public MySQL packages, and it contains a long list of breaking changes. One of them affects error handling in ordinary application code: client methods that return a promise now reject instead of throwing at the call.

Breaking changes in application code

On postgres(), sqlite(), a supabase() role-bound client and a transaction, the affected members are query, execute, prepare, transaction, ORM calls, and a prepared statement's query and execute. A closed client, a failed connection or an ended transaction now shows up through the returned promise or result. Error codes and messages are unchanged. .catch(handle) and Promise.allSettled now see these errors. A try/catch around a call without await no longer catches them, so developers need to add await. db.runtime() still throws at the call, as does a mistake in the call itself.

Two related connection changes arrive in the same release. When a developer passes their own pg.Pool or pg.Client to postgres() or supabase(), close() now also closes the client's runtime. The pool or client itself stays open and must be ended separately. A SQLite file that cannot be opened is now a SqlConnectionError rather than a SqlQueryError.

PostgreSQL date and time handling changes the most. In PSL, Timestamptz, Timestamp and Interval become TimestamptzTemporal, TimestampTemporal and IntervalParts, and the old names are errors. The renamed types read the same values with the same codec, so contract hashes do not change. IntervalParts columns are now typed as the PgInterval object ({ months, days, micros }) instead of a string. The text types (TimestamptzString, TimestampString, DateString, TimeString and Timetz) now read and write ISO 8601 on every server. A value that read 2024-01-01 01:00:00+01 on a Europe/Berlin server now reads 2024-01-01T00:00:00Z.

The driver sets DateStyle=ISO on every connection, including pools and clients passed in by the developer. A connection that prints another style fails every date and time read with RUNTIME.DATE_STYLE_NOT_ISO. Literal defaults on these types must also use ISO form, or contract emit refuses them with CONTRACT.DEFAULT_INVALID.

Field presets are gone. Calls such as id.uuidv4String() and temporal.createdAt() in PSL, and field.id.uuidv4String() and field.generated(...) in TypeScript, are replaced by type constructors and aliases. A Post model that used id.ulid() is now written Ulid @id @onCreate(ulid()). The replacements store the same contract with the same hashes, so nothing needs re-emitting, signing or migrating. PSL reports a leftover preset with PSL_UNRESOLVED_REFERENCE and a message giving the exact rewrite.

A contract built from a Prisma 7 schema now treats @default(now()) as @default(now()) @onCreate(now()). Creates through Prisma 8 therefore write the current time in UTC from the client, as Prisma 7's client did. Previously the database clock filled the column, in the session's time zone. Re-emitting the contract changes only the execution hash. After upgrading, the release notes advise running prisma contract emit once.

Enum values and MongoDB

On Postgres and SQLite, removing an enum member or changing its stored value now triggers a question in migration plan and db update. The answer rewrites the rows that hold the old value. Scripts without a terminal fail with CLI.CONSENT_REQUIRED until they pass --rename Status.ARCHIVED:Status.RETIRED or --delete Status.ARCHIVED.

A rename changes the stored value in every column and list that holds it. A delete sets the value to NULL in optional columns and removes it from lists. If a required column still holds the value, the apply stops before changing anything and explains how to keep the data. Before this change, Postgres failed at apply when a row held the old value, and SQLite planned nothing. Native Postgres enums (pg.enum(...)) are unchanged, and migration.ts gains this.replaceStoredValue and this.removeStoredValue.

MongoDB contracts written in TypeScript, PSL or Prisma 6 now go through one builder and follow the same rules. mongoContract(schemaPath, options) requires options.target, and ContractDefinition is renamed ContractInput. MongoModelDefinition is removed in favor of ContractModel<MongoModelStorage>. The shared rules mean some contracts are now refused. These include optional or list discriminators and variants stored in a different collection than their base. A Prisma 6 schema with two identical indexes in one collection now yields one index, so its storage hash changes and the developer must run prisma contract emit and then prisma db sign.

MySQL preview, fixes and tooling

MySQL gets its first public packages, @prisma/orm-mysql and @prisma/orm-target-mysql, as an early preview. They can emit a contract. migration plan, db migrate, db verify and db sign work against MySQL 8.4 for creating and dropping tables, columns, indexes, unique constraints and foreign keys. Other schema changes are refused as unsupported, and queries are not available yet. A prisma-8-demo-mysql example shows the workflow. When a migration fails on a target that cannot roll back schema changes, such as MySQL or MongoDB, the error names the failed contract space and what stayed applied. Running db migrate again skips the applied steps and finishes the rest.

When a database's marker points to a contract that no migration starts or ends at, db migrate fails with MIGRATION.MARKER_MISMATCH. The error now gives the last contract of the migration graph the database reached, and lists applied migrations that are missing from disk or were replanned. Its suggested recoveries are db update for databases whose history need not be kept, and new migrations for databases such as production. The previous suggestions, migration plan --from and migration ref set, were commands the CLI itself refuses. The release says no error, document or skill recommends resetting a database.

Other fixes include SQLite queries that bound parameters to the wrong placeholders. This happened with parameters in a join or derived table plus WHERE, or in GROUP BY plus HAVING or ORDER BY, and could return wrong rows. The Postgres driver now identifies lost connections from error codes and driver state, not message text. Temporal fields that were typed any without a polyfill are now typed through the temporal-spec package. The upgrade guides for rc.18 to rc.19 and rc.19 to rc.20 now work on real projects through prisma skills sync.

Extension authors face a separate set of changes. Targets must name a markerLocation, the authoring.field namespace is removed, and drivers must apply target-declared session settings. The release links separate upgrade recipes for apps and for extensions, and each breaking change lists the change ids to look for in them.