prisma-8

Use when working in a project that depends on @prisma/orm-postgres, @prisma/orm-sqlite, or @prisma/orm-mongo (Prisma 8, formerly Prisma Next): editing contract.prisma or a contract.ts builder, running `prisma contract emit`, planning or applying migrations, editing migration.ts, writing db.orm / db.sql / db.query queries, wiring db.ts or middleware, integrating a build tool, using the Supabase extension or RLS, or reading a dotted error code such as MIGRATION.HASH_MISMATCH. Use when the user asks "what is Prisma 8", "where do I start", or compares it to another ORM. Use when the user asks to upgrade or bump Prisma 8 in an app or an extension package. Use when you see @internal/* or @prisma/orm-* imports, prisma.config.ts with definePrismaConfig, or contract.json / contract.d.ts. Do not use for Prisma ORM 7 or earlier (schema.prisma + @prisma/client).

Install
npx skills add 'https://github.com/prisma/orm/tree/main/skills/prisma-8'
Download bundle ↓
main · fac8604Scanned 2026-09-17

Contributors

GitHub-linked commit authors for this SKILL.md at the saved revision. Co-authors and history before file renames are not included.

File history ↗

upgrading/app/upgrades/0.12-to-0.13/instructions.md

upgrading/app/upgrades/0.12-to-0.13/instructions.mdBrowse 76 files
View on GitHub
← Back to SKILL.md

from: "0.12" to: "0.13" changes:

  • id: sqlite-create-table-method summary: | SQLite migrations: createTable is no longer a free function exported from @internal/sqlite/migration. It is now a protected method on the Migration base class. Replace every free createTable(...) call in your SQLite migration files with this.createTable({ table: ..., columns: [...], constraints: [...] }). The col(), lit(), fn(), primaryKey(), foreignKey(), and unique() builder helpers are now exported from @internal/sqlite/migration directly, so your import line stays a single entry point. detection: glob: "**/migration.ts" contains: - "createTable" - "@internal/sqlite/migration" anyMatch: false
  • id: re-emit-mti-variant-link-columns summary: | MTI variant models — PSL @@base(Parent, "tag") models that carry their own @@map and are therefore stored in their own table — now materialise base-PK link columns in storage. On re-emit, each such variant table gains a copy of the base table's full primary-key column set (same names and types), a primary key over those columns, and a cascading foreign key referencing the base table's primary key; the contract's storageHash changes accordingly. Re-emit your contract artefacts (pnpm emit), then advance your database with the corresponding migration (prisma-next migration planprisma-next migrate) so the variant tables gain the link column, PK, and cascading FK. Contracts whose variants share the base table (single-table inheritance, no own @@map) are unaffected. detection: glob: "**/contract.json" contains: - '"base":' anyMatch: true script: ./re-emit-mti-variant-link-columns.ts
  • id: cross-space-fk-psl-pattern summary: | New opt-in PSL authoring pattern: reference a model from an extension contract space (e.g. supabase:auth.AuthUser) in a relation field and declare a named-type alias (types { Uuid = String @db.Uuid }) for database-native types. No action required for consumers who do not use cross-space foreign keys; this entry documents the pattern for new adopters.
  • id: storage-namespace-envelope-re-emit summary: | The storage IR in contract.json moved to a namespace envelope (storage.namespaces.<ns>.entries.<kind>). This changes storageHash for every SQL and Mongo contract. Re-emit your contract artefacts (prisma-next contract emit), then plan and apply the corresponding migration (prisma-next migration planprisma-next migrate) so your database schema is reconciled with the new contract shape. No source change is required — re-emitting is sufficient. detection: glob: "**/contract.json" anyMatch: true
  • id: telemetry-now-opt-out summary: | Telemetry is now opt-out by default. If you previously relied on the opt-in default to keep telemetry off, set PRISMA_NEXT_DISABLE_TELEMETRY=1 or DO_NOT_TRACK=1 in your environment to restore that behaviour. No code change is required.

0.12 → 0.13 — User upgrade instructions

sqlite-create-table-method

Starting at this release, createTable is no longer a free function exported from @internal/sqlite/migration. It is now a protected method on the Migration base class — call it as this.createTable({...}) inside get operations().

The column builder helpers col(), lit(), fn(), primaryKey(), foreignKey(), and unique() are now exported from @internal/sqlite/migration directly, so you do not need an additional import.

Before 0.13

import { Migration, MigrationCLI, createTable, col, primaryKey } from '@internal/sqlite/migration';

export default class M extends Migration {
  override describe() { return { from: null, to: '...' }; }

  override get operations() {
    return [
      createTable('user', [
        col('id', 'INTEGER', { primaryKey: true }),
        col('email', 'TEXT', { notNull: true }),
      ]),
    ];
  }
}

MigrationCLI.run(import.meta.url, M);

Starting at 0.13

import { Migration, MigrationCLI, col, primaryKey } from '@internal/sqlite/migration';

export default class M extends Migration {
  override describe() { return { from: null, to: '...' }; }

  override get operations() {
    return [
      this.createTable({
        table: 'user',
        columns: [
          col('id', 'INTEGER', { primaryKey: true }),
          col('email', 'TEXT', { notNull: true }),
        ],
      }),
    ];
  }
}

MigrationCLI.run(import.meta.url, M);

Migration steps

  1. Remove createTable from the import list for @internal/sqlite/migration.
  2. In get operations(), replace each createTable(tableName, columns, constraints?) call with this.createTable({ table: tableName, columns, constraints? }).
  3. Run pnpm typecheck && pnpm test to confirm the migration compiled and all tests pass.

TypeScript flags the removed createTable import as an error after the bump, so every affected call site is pinpointed at compile time. No contract re-emit is required — this is an authoring-surface change only.

Starting at this release, a Multi-Table Inheritance (MTI) variant model stores an explicit link to its base row. An MTI variant is a PSL model that declares @@base(Parent, "tag") and carries its own @@map, so it lives in a dedicated table rather than sharing the base table:

model Task {
  id   String @id @default(uuid())
  type String
  // …
  @@discriminator(type)
  @@map("task")
}

model Bug {
  severity String
  @@base(Task, "bug")
  @@map("bug")
}

Before this release, the bug table held only the variant-specific columns (severity, …) with no primary key and no relationship to task. From this release on, re-emitting the contract materialises the base-PK link in the variant's storage table:

  • a copy of the base table's full primary-key column set — the same column names and types (one column for a single-column PK like id, or every component for a composite PK),
  • a primary key over those link columns,
  • a cascading foreign key (ON DELETE CASCADE) from those columns to the base table's matching primary-key columns.

The variant row's link columns mirror its parent base row's primary key — the same identity links a task row to its bug/feature detail row. This is the storage shape the runtime already assumed when writing base + variant rows together; the change makes it explicit and enforced at the database level.

Single-table inheritance variants — @@base(...) models without their own @@map, which share the base table — are unaffected: there is no separate table to link.

Re-emit your contracts

Run the colocated script from your project root:

pnpm exec tsx ./re-emit-mti-variant-link-columns.ts

It walks the project for prisma.config.ts directories, resolves each space's committed contract.json, and re-emits any contract whose MTI variant table still lacks its link column (an MTI variant model whose storage table has no primaryKey). It prefers a package's emit script when present, otherwise runs prisma-next contract emit --config <path>.

Use --check for a dry-run that lists the contract-spaces still needing re-emit and exits non-zero if any remain:

pnpm exec tsx ./re-emit-mti-variant-link-columns.ts --check

The regenerated contract.json gains the variant's link columns (the base PK's column set), their primary key, and the cascading foreign key under storage.namespaces.<ns>.tables.<variant>, and the contract's storageHash changes. contract.d.ts picks up the new columns on the variant's row type.

Migrate your database

Re-emitting changes storageHash, so your live database needs the matching schema change. Plan and apply it:

prisma-next migration plan --name mti-variant-link-columns
prisma-next migrate

The plan adds the variant's link columns, sets them NOT NULL, adds the primary key over them, and adds the cascading foreign key to the base table.

A variant row's link columns must equal its parent base row's primary key — that shared identity is what links a task row to its bug/feature detail row, and the cascading foreign key to the base table enforces it. There is therefore no correct backfill, and you must never fabricate the link values (for example with gen_random_uuid()): fabricated values have no matching base row, so the validating foreign key in this same migration would immediately reject them.

The runtime always wrote each variant row together with its base row, sharing the same primary-key values. On a database provisioned that way there are no rows missing the link columns, so the SET NOT NULL step is a no-op and the migration applies cleanly with no backfill. Author the migration with no dataTransform — just addColumn (nullable) → setNotNull → primary key → foreign key — then run node <migration>.ts (or pnpm exec tsx <migration>.ts) to self-emit ops.json and attest the package before prisma-next migrate.

If your database does hold variant rows that predate the link columns, they are unlinkable orphans — nothing in those rows maps them back to their base row. The SET NOT NULL precheck ("ensure no NULL values") halts the migration before any destructive step. Resolve those rows by hand — map each to the correct base primary key, or delete it — and re-run. Do not paper over the halt with fabricated link values.

Validation

After re-emitting and migrating, run pnpm typecheck && pnpm test (or your application's equivalent), then prisma-next migration check to confirm the on-disk chain is consistent. Inspect the contract.json diff: each MTI variant table should carry the base PK's link columns, a primaryKey over them, and a cascading foreignKey to its base table.

cross-space-fk-psl-pattern

This release ships PSL support for referencing a model from an extension contract space (e.g. supabase:auth.AuthUser) in a relation field, together with named-type aliases for database-native column types.

This entry is informational. No existing consumer has cross-space foreign keys to change — this is a new opt-in capability. Adopt it when you want a field in your model to reference a row owned by an extension (such as Supabase's auth.users table).

Named-type aliases

Declare a types block at the top of your contract.prisma to give a database-native type a reusable name:

types {
  Uuid = String @db.Uuid
}

You can then use Uuid as a field type anywhere in the same contract. On emit the column receives nativeType: "uuid" in contract.json.

Cross-space relation field

Reference another contract space's model using the <space>:<namespace>.<Model> syntax in a relation field. The relation requires extensionPacks to declare the dependency on the space:

// Before (no cross-space FK)
namespace public {
  model Profile {
    id       String @id @default(uuid())
    username String
    @@map("profile")
  }
}

// After (cross-space FK to supabase:auth.AuthUser)
types {
  Uuid = String @db.Uuid
}

namespace public {
  model Profile {
    id       String @id @default(uuid())
    username String
    userId   Uuid   @unique
    user     supabase:auth.AuthUser @relation(fields: [userId], references: [id], onDelete: Cascade)
    @@map("profile")
  }
}

On emit, contract.json gains:

  • A types.Uuid entry under storage for the named-type alias.
  • The userId column with typeRef: "Uuid" on the storage table.
  • A cross-space foreignKey entry on the storage table pointing at the extension space's table.

Run prisma-next contract emit after updating contract.prisma, then plan and apply the migration (prisma-next migration plan --name add-user-fk && prisma-next migrate) to add the column and foreign key to your database.

storage-namespace-envelope-re-emit

The storage IR inside contract.json moved to a namespace envelope in 0.13. Every table and type entry that was previously at the top level of storage now lives under storage.namespaces.<ns>.entries.<kind>. Cross-references that were bare strings are now { namespace, model } objects in domain. The emitter handles this automatically — no schema source change is needed.

Because the shape change affects storageHash, every SQL and Mongo contract must be re-emitted, and the database must be migrated to match.

Re-emit your contract

prisma-next contract emit

Migrate your database

prisma-next migration plan --name storage-namespace-envelope
prisma-next migrate

The migration records the hash transition; no column or table is added or removed — this is a metadata-only change. Confirm with prisma-next migration check once done.

telemetry-now-opt-out

Informational — no code change required.

Starting at 0.13, the CLI collects anonymised usage telemetry by default (previously opt-in). If you want to disable it, set either of the following environment variables:

PRISMA_NEXT_DISABLE_TELEMETRY=1
# or
DO_NOT_TRACK=1

Either variable takes effect immediately — no config file change needed. See Telemetry for what is collected and how to opt out permanently.