github prisma/orm v8.0.0-rc.22

pre-release3 hours ago

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, Date and Time are convenience types with new defaults. Each is named for what a field means and becomes one explicit type, chosen by its optional as, storage and precision arguments. At its default, DateTime is a timestamptz(3) column read as a JavaScript Date, Date reads a string such as "2024-02-29", Time reads a string such as "09:30:00", and the new Duration reads an ISO 8601 duration such as "P1M". Until now the three names read Temporal values. To keep a field as it is, rename DateTime to TimestamptzTemporal, Date to DateTemporal and Time to TimeTemporal; in TypeScript, call type.TimestamptzTemporal(...), type.DateTemporal() and type.TimeTemporal(...). temporal.CreatedAt and temporal.UpdatedAt are now built on the new DateTime, and their String and JsDate variants are removed. A contract read by prisma7Schema(...) now reads a Prisma 7 DateTime field as a Date, as Prisma 7 did, so run prisma db sign after re-emitting it. On PostgreSQL and MySQL, a column read as a Date, such as MySQL's DateTime, 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 infer and contract print now write the convenience types: a timestamptz(3) column is DateTime, a timestamptz column DateTime(precision: 6), a timestamp(3) column DateTime(storage: timestamp), date is Date, time is Time and interval is Duration. So a contract you infer again reads a timestamptz field as a Date, where it read an ISO 8601 string, and drops its microseconds; add as: string, as in DateTime(as: string, precision: 6), to keep the string. See psl-date-time-types-are-convenience-types, ts-date-time-helpers-are-convenience-types, temporal-type-aliases-on-datetime, prisma7-schema-reads-dates and date-default-below-a-millisecond in 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 next migration plan adds them, and db migrate fails on a row that does not fit. A contract.json that keeps valueObjects in the domain plane is refused with CONTRACT.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: ValueObjectTypesFromDomain is now ValueObjectTypesFromStorage, FieldTypesFromDomain takes the storage namespaces as a sixth argument, ValueObjectFieldType is renamed ValueObjectReference, and a Postgres codec descriptor declares jsonType. See value-objects-in-storage, value-object-types-from-storage, postgres-value-object-checks and discriminator-typed-by-variant-values in the app recipe, and mongo-result-shapes-read-storage, postgres-check-expression-types-move, field-types-read-storage-value-objects, value-object-reference-type and postgres-codec-json-type in the extension recipe. (#30869)

  • MongoDB's query builder, raw lane, ORM and runtime take an execution context. mongoQuery, mongoRaw and mongoOrm take { context } instead of { contract }, as sql({ context }) and the SQL ORM do. A context comes from mongoStatic({ contract }) or mongo({ contract }), which validate the contract once. mongoOrm takes { context, runtime } and no longer takes executor, enums or mutationDefaults. createMongoRuntime takes { context, adapter, driver }. buildMongoEnums, MongoEnumAccessors, MongoOrmEnums and ORM.MUTATION_DEFAULTS_MISSING are removed. Extension authors: createSqlExecutionStack(...) returns SqlExecutionStackWithDriver, whose driver is always set, only when given a driver. See mongo-query-takes-context, mongo-orm-takes-context and mongo-runtime-takes-adapter in the app recipe, and the same ids plus sql-stack-with-driver-requires-driver in 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. createMongoDriver and MongoDriverImpl are 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 calls mongo({ contract, url }) sees no difference. Extension authors: MongoDriver gains connect(binding), and code that only sends commands takes the new MongoQueryable. See mongo-driver-from-runtime-descriptor in the app recipe, and mongo-driver-from-runtime-descriptor and mongo-driver-connect in 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 emit writes each pack's files. Each pack's contract now declares its extension, and its head no longer requires an install invariant. prisma contract emit writes each composed pack's head ref, contract snapshot and migrations under migrations/<space-id>/, as migration plan already did: commit them, then run prisma db migrate on each database to move the pack's marker. db sign now refuses a contract space whose head requires an invariant its marker does not record, with MIGRATION.SIGN_INVARIANTS_MISSING. Extension authors: declare a pack's extension in its contract instead of installing it with installExtension(...) and an invariant. See contract-emit-pins-pack-files and packs-declare-their-postgres-extension in the app recipe, and declare-postgres-extension-in-pack-contract in 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 in contract emit. Install one version of every @prisma/orm-* package and a prisma CLI that runs it. See one-orm-version in 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 @unique and @@index on 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 with MIGRATION.PRECHECK_FAILED. Re-emit the contract, and replace a planned migration that creates both indexes. Extension authors: BuildRefusal loses the kind indexDuplicate, and indexLookupKey, MONGO_INDEX_OPTION_KEYS, MongoIndexOptionKey and canonicalize move from @prisma/orm-family-mongo/schema-ir to @prisma/orm-family-mongo/contract. See mongo-merged-redundant-indexes in the app recipe, and mongo-build-refusal-index-duplicate-removed and mongo-index-lookup-key-moved in the extension recipe. (#30894)

Features

  • connect() without options on a postgres(), sqlite(), mysql() or mongo() 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 with DRIVER.ALREADY_CONNECTED, now with the message <Target> client is already bound. See client-connect-safe-to-call-again in the app recipe. (#30899)
  • A Postgres contract can declare the extensions it needs, with extension pg_trgm {} in PSL or extension('pg_trgm') in TypeScript, at the top level or in a namespace. db verify checks them, the planner creates them, and contract infer and contract print write them. The Prisma 7 reader reads the datasource extensions list, so a Prisma 7 database that uses pgvector, PostGIS or ParadeDB adopts Prisma 8 with contract emit and db sign. (#30902)
  • A new package, @prisma/orm-postgres-contrib, holds a citext extension pack: the PSL type citext.Citext, which compares case-insensitively. The Prisma 7 reader reads @db.Citext as citext.Citext when the config composes the pack, and refuses it with PSL.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, Oid and Money, and the Prisma 7 reader reads @db.Bit, @db.VarBit, @db.Xml, @db.Oid and @db.Money. The Postgres driver sets lc_monetary=C on every connection and writes money through numeric, so money reads 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/runtime and @prisma/orm-mysql/static. The MySQL drivers run on the mariadb package 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 now generator of a timestamp column read as a Date or a Temporal value rounds the time down to the column's precision, so the value a create returns is the value PostgreSQL stores. Before, a timestamp(0) column could store a time one second after the write. Re-emit the contract; only the execution hash changes. See date-now-takes-column-precision in 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 as String for @db.Text, instead of a type that does not exist. (#30909)

Don't miss a new orm release

NewReleases is sending notifications on new releases.