v8.0.0-rc.22
On PostgreSQL, DateTime, Date, Time and the new Duration are convenience types whose defaults read a JavaScript Date or a string, with no Temporal polyfill. Value objects move into the storage plane, so changing one is a migration, and Postgres checks value object and discriminator columns. MongoDB's query builder, raw lane, ORM and runtime take an execution context, as SQL's do, and a MongoDB client creates its driver from the execution stack. A contract can declare the Postgres extensions it needs, so db sign adopts a database that uses pgvector, PostGIS, ParadeDB or the new citext pack. The Prisma 7 reader now reads every Postgres @db.* type Prisma 7 accepts. MySQL gets ORM and SQL builder reads. Prisma ORM refuses to run from two installed copies, and connect() on a client is safe to call again.
The upgrade recipes for this hop: the app recipe and the extension recipe. Each breaking change below names the change ids to look for in them. The app recipe opens with the order to do the steps in. After upgrading, run prisma contract emit once, so a contract that an extension such as Supabase stamps with its version matches the installed package (contract-artifacts-restamp in the app recipe).
Breaking changes
-
On PostgreSQL,
DateTime,DateandTimeare convenience types with new defaults. Each is named for what a field means and becomes one explicit type, chosen by its optionalas,storageandprecisionarguments. At its default,DateTimeis atimestamptz(3)column read as a JavaScriptDate,Datereads a string such as"2024-02-29",Timereads a string such as"09:30:00", and the newDurationreads an ISO 8601 duration such as"P1M". Until now the three names readTemporalvalues. To keep a field as it is, renameDateTimetoTimestamptzTemporal,DatetoDateTemporalandTimetoTimeTemporal; in TypeScript, calltype.TimestamptzTemporal(...),type.DateTemporal()andtype.TimeTemporal(...).temporal.CreatedAtandtemporal.UpdatedAtare now built on the newDateTime, and theirStringandJsDatevariants are removed. A contract read byprisma7Schema(...)now reads a Prisma 7DateTimefield as aDate, as Prisma 7 did, so runprisma db signafter re-emitting it. On PostgreSQL and MySQL, a column read as aDate, such as MySQL'sDateTime, refuses a default with digits below a millisecond. The rest of this change is PostgreSQL only: SQLite, MySQL and MongoDB keep their date and time types.contract inferandcontract printnow write the convenience types: atimestamptz(3)column isDateTime, atimestamptzcolumnDateTime(precision: 6), atimestamp(3)columnDateTime(storage: timestamp),dateisDate,timeisTimeandintervalisDuration. So a contract you infer again reads atimestamptzfield as aDate, where it read an ISO 8601 string, and drops its microseconds; addas: string, as inDateTime(as: string, precision: 6), to keep the string. Seepsl-date-time-types-are-convenience-types,ts-date-time-helpers-are-convenience-types,temporal-type-aliases-on-datetime,prisma7-schema-reads-datesanddate-default-below-a-millisecondin the app recipe. (#30847)Before:
model Event { id Int @id at DateTime day Date opensAt Time(0) }
After, to keep each field and its contract:
model Event { id Int @id at TimestamptzTemporal day DateTemporal opensAt TimeTemporal(0) }
-
Value objects are storage entities, and Postgres checks their columns. A contract keeps its value objects at
storage.namespaces.<ns>.entries.valueObject, and a field names one by its storage coordinate, so changing a value object changes the storage hash and is a migration. On SQL, a discriminator column names a value set of its variants' values, and a discriminator field is typed by those values, such as'bug' | 'feature'. On a managed Postgres table, the contract derives a check for each value object column and each discriminator column; the nextmigration planadds them, anddb migratefails on a row that does not fit. Acontract.jsonthat keepsvalueObjectsin the domain plane is refused withCONTRACT.VALIDATION_FAILED. The app recipe ships a script that rewrites every contract, snapshot and migration that names one; run it, then sign each database before you re-emit. Extension authors:ValueObjectTypesFromDomainis nowValueObjectTypesFromStorage,FieldTypesFromDomaintakes the storage namespaces as a sixth argument,ValueObjectFieldTypeis renamedValueObjectReference, and a Postgres codec descriptor declaresjsonType. Seevalue-objects-in-storage,value-object-types-from-storage,postgres-value-object-checksanddiscriminator-typed-by-variant-valuesin the app recipe, andmongo-result-shapes-read-storage,postgres-check-expression-types-move,field-types-read-storage-value-objects,value-object-reference-typeandpostgres-codec-json-typein the extension recipe. (#30869) -
MongoDB's query builder, raw lane, ORM and runtime take an execution context.
mongoQuery,mongoRawandmongoOrmtake{ context }instead of{ contract }, assql({ context })and the SQL ORM do. A context comes frommongoStatic({ contract })ormongo({ contract }), which validate the contract once.mongoOrmtakes{ context, runtime }and no longer takesexecutor,enumsormutationDefaults.createMongoRuntimetakes{ context, adapter, driver }.buildMongoEnums,MongoEnumAccessors,MongoOrmEnumsandORM.MUTATION_DEFAULTS_MISSINGare removed. Extension authors:createSqlExecutionStack(...)returnsSqlExecutionStackWithDriver, whosedriveris always set, only when given a driver. Seemongo-query-takes-context,mongo-orm-takes-contextandmongo-runtime-takes-adapterin the app recipe, and the same ids plussql-stack-with-driver-requires-driverin the extension recipe. (#30865)Before:
import { mongoQuery } from '@prisma/orm-mongo/query-builder'; const query = mongoQuery({ contract }); const raw = mongoRaw({ contract });
After:
import mongoStatic from '@prisma/orm-mongo/static'; const { query, raw } = mongoStatic({ contract });
-
A MongoDB driver is created from its descriptor.
createMongoDriverandMongoDriverImplare no longer exported. Put the runtime driver descriptor, the default export of@prisma/orm-mongo/driver/runtime, on the execution stack, create the driver from the stack, and connect it. An application that callsmongo({ contract, url })sees no difference. Extension authors:MongoDrivergainsconnect(binding), and code that only sends commands takes the newMongoQueryable. Seemongo-driver-from-runtime-descriptorin the app recipe, andmongo-driver-from-runtime-descriptorandmongo-driver-connectin the extension recipe. (#30906)Before:
import { createMongoDriver } from '@prisma/orm-mongo/driver'; const driver = await createMongoDriver(url, dbName);
After:
import mongoRuntimeDriver from '@prisma/orm-mongo/driver/runtime'; const stack = createMongoExecutionStack({ target, adapter, driver: mongoRuntimeDriver }); const driver = stack.driver.create(); await driver.connect({ kind: 'url', url, dbName });
-
The pgvector, PostGIS and ParadeDB packs declare their Postgres extension, and
contract emitwrites each pack's files. Each pack's contract now declares its extension, and its head no longer requires an install invariant.prisma contract emitwrites each composed pack's head ref, contract snapshot and migrations undermigrations/<space-id>/, asmigration planalready did: commit them, then runprisma db migrateon each database to move the pack's marker.db signnow refuses a contract space whose head requires an invariant its marker does not record, withMIGRATION.SIGN_INVARIANTS_MISSING. Extension authors: declare a pack's extension in its contract instead of installing it withinstallExtension(...)and an invariant. Seecontract-emit-pins-pack-filesandpacks-declare-their-postgres-extensionin the app recipe, anddeclare-postgres-extension-in-pack-contractin the extension recipe. (#30902) -
Prisma ORM refuses to run from two installed copies. Every command and client now stops with
RUNTIME.MULTIPLE_ORM_COPIES, naming each copy and where it is, when more than one copy of Prisma ORM is installed. Before, such a project could fail later with an internal error, for example on the first literal default incontract emit. Install one version of every@prisma/orm-*package and aprismaCLI that runs it. Seeone-orm-versionin the app recipe. (#30883) -
MongoDB merges identical and redundant indexes in a collection. A unique and a plain index on the same keys, such as
@uniqueand@@indexon one field, now give one unique index, and identical indexes, which TypeScript and PSL refused before, give one. Before, the contract held both indexes of such a pair and the migration that created them failed withMIGRATION.PRECHECK_FAILED. Re-emit the contract, and replace a planned migration that creates both indexes. Extension authors:BuildRefusalloses the kindindexDuplicate, andindexLookupKey,MONGO_INDEX_OPTION_KEYS,MongoIndexOptionKeyandcanonicalizemove from@prisma/orm-family-mongo/schema-irto@prisma/orm-family-mongo/contract. Seemongo-merged-redundant-indexesin the app recipe, andmongo-build-refusal-index-duplicate-removedandmongo-index-lookup-key-movedin the extension recipe. (#30894)
Features
connect()without options on apostgres(),sqlite(),mysql()ormongo()client is safe to call again: it waits for a connection in progress, or resolves with the runtime the client's queries already use.connect(options)on a bound client still rejects withDRIVER.ALREADY_CONNECTED, now with the message<Target> client is already bound. Seeclient-connect-safe-to-call-againin the app recipe. (#30899)- A Postgres contract can declare the extensions it needs, with
extension pg_trgm {}in PSL orextension('pg_trgm')in TypeScript, at the top level or in a namespace.db verifychecks them, the planner creates them, andcontract inferandcontract printwrite them. The Prisma 7 reader reads the datasourceextensionslist, so a Prisma 7 database that uses pgvector, PostGIS or ParadeDB adopts Prisma 8 withcontract emitanddb sign. (#30902) - A new package,
@prisma/orm-postgres-contrib, holds acitextextension pack: the PSL typecitext.Citext, which compares case-insensitively. The Prisma 7 reader reads@db.Citextascitext.Citextwhen the config composes the pack, and refuses it withPSL.PRISMA7_EXTENSION_PACK_MISSING, naming the import to add, when it does not. Every Postgres@db.*type Prisma 7 accepts is now read. (#30904) - PostgreSQL gains the types
Xml,OidandMoney, and the Prisma 7 reader reads@db.Bit,@db.VarBit,@db.Xml,@db.Oidand@db.Money. The Postgres driver setslc_monetary=Con every connection and writesmoneythroughnumeric, somoneyreads and writes the same value in any locale. (#30900) - MySQL gets ORM and SQL builder reads, including nested and ordered includes, through
@prisma/orm-mysql/runtimeand@prisma/orm-mysql/static. The MySQL drivers run on themariadbpackage and send every value as a server-side prepared-statement parameter. (#30863, #30893) - Prisma's extension packages list the npm keyword
prisma-orm-extension, so a search for it finds them next to community extensions. (#30892)
Fixes
- On PostgreSQL, the
nowgenerator of a timestamp column read as aDateor aTemporalvalue rounds the time down to the column's precision, so the value a create returns is the value PostgreSQL stores. Before, atimestamp(0)column could store a time one second after the write. Re-emit the contract; only the execution hash changes. Seedate-now-takes-column-precisionin the app recipe. (#30847) - The diagnostic for a Prisma 7
@db.*attribute in a Prisma 8 schema suggests the constructor Prisma 8 renamed it to, such asStringfor@db.Text, instead of a type that does not exist. (#30909)