@prisma/orm-mongo 8.0.0-rc.1-dev.46 → 8.0.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -5
- package/dist/contract-builder.d.mts +1 -0
- package/dist/contract-builder.d.mts.map +1 -1
- package/dist/utils__suppress-idle-connection-errors.d.mts +1 -0
- package/dist/utils__suppress-idle-connection-errors.mjs +2 -0
- package/package.json +14 -12
- package/skills/prisma-8/SKILL.md +96 -0
- package/skills/prisma-8/references/build.md +142 -0
- package/skills/prisma-8/references/contract.md +446 -0
- package/skills/prisma-8/references/debug.md +144 -0
- package/skills/prisma-8/references/feedback.md +251 -0
- package/skills/prisma-8/references/migration-model.md +175 -0
- package/skills/prisma-8/references/migration-review.md +241 -0
- package/skills/prisma-8/references/migrations.md +538 -0
- package/skills/prisma-8/references/queries-mongo.md +236 -0
- package/skills/prisma-8/references/queries-postgres.md +415 -0
- package/skills/prisma-8/references/queries.md +237 -0
- package/skills/prisma-8/references/quickstart.md +317 -0
- package/skills/prisma-8/references/runtime.md +366 -0
- package/skills/prisma-8/references/supabase.md +231 -0
- package/skills/prisma-8/references/upgrade-app.md +101 -0
- package/skills/prisma-8/references/upgrade-extension.md +105 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.10-to-0.11/instructions.md +56 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/instructions.md +381 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/re-emit-closed-mongo-contracts.ts +202 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/re-emit-domain-namespaced-contracts.ts +201 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/re-emit-postgres-public-default.ts +198 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/strip-migration-labels-hints.ts +340 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.12-to-0.13/instructions.md +339 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.12-to-0.13/re-emit-mti-variant-link-columns.ts +229 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.13-to-0.14/instructions.md +543 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.13-to-0.14/migration-op-factories-to-methods.ts +290 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.13-to-0.14/uuid-preset-rename.ts +43 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.14-to-0.15/instructions.md +359 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.15-to-0.16/instructions.md +173 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.16-to-0.17/instructions.md +805 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.16-to-0.17/strip-sha256-hash-prefixes.ts +382 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.17-to-8.0.0-rc.1/instructions.md +72 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.7-to-0.8/instructions.md +8 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.8-to-0.9/instructions.md +36 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.8-to-0.9/strip-inline-contracts.ts +226 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.9-to-0.10/instructions.md +86 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.9-to-0.10/stamp-storage-types-kind.ts +360 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.1-to-8.0.0-rc.2/instructions.md +588 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.2-to-8.0.0-rc.3/instructions.md +5 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.3-to-8.0.0-rc.4/instructions.md +158 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.4-to-8.0.0-rc.5/instructions.md +42 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.5-to-8.0.0-rc.6/instructions.md +157 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.6-to-8.0.0-rc.7/instructions.md +33 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.7-to-8.0.0-rc.8/instructions.md +36 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.8-to-8.0.0-rc.9/instructions.md +63 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.9-to-8.0.0-rc.10/instructions.md +84 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.10-to-0.11/instructions.md +276 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.11-to-0.12/instructions.md +738 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.11-to-0.12/migrate-contract-testing-imports.ts +97 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.11-to-0.12/regenerate-extension-public-baseline.ts +223 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.11-to-0.12/strip-migration-labels-hints.ts +340 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.12-to-0.13/instructions.md +266 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.13-to-0.14/instructions.md +522 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.13-to-0.14/migration-op-factories-to-methods.ts +290 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.13-to-0.14/uuid-preset-rename.ts +43 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.14-to-0.15/instructions.md +803 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.15-to-0.16/instructions.md +219 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.16-to-0.17/instructions.md +731 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.16-to-0.17/strip-sha256-hash-prefixes.ts +382 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.17-to-8.0.0-rc.1/instructions.md +194 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.7-to-0.8/instructions.md +8 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.8-to-0.9/instructions.md +57 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.8-to-0.9/strip-inline-contracts.ts +226 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.9-to-0.10/instructions.md +150 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.9-to-0.10/stamp-storage-types-kind.ts +360 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.1-to-8.0.0-rc.2/instructions.md +746 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.2-to-8.0.0-rc.3/instructions.md +5 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.3-to-8.0.0-rc.4/instructions.md +137 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.4-to-8.0.0-rc.5/instructions.md +129 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.5-to-8.0.0-rc.6/instructions.md +102 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.6-to-8.0.0-rc.7/instructions.md +33 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.7-to-8.0.0-rc.8/instructions.md +35 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.8-to-8.0.0-rc.9/instructions.md +86 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.9-to-8.0.0-rc.10/instructions.md +63 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
|
|
2
|
+
# Prisma 8 — Queries
|
|
3
|
+
|
|
4
|
+
> **Edit your data contract. Prisma handles the rest.**
|
|
5
|
+
|
|
6
|
+
Once the contract is emitted and the DB is up to date, this skill covers everything you do *with* the data: reading, writing, eager-loading relations, aggregating, and the choice between the ORM and the lower-level query lane.
|
|
7
|
+
|
|
8
|
+
## When to Use
|
|
9
|
+
|
|
10
|
+
- User wants to read, write, update, or delete data.
|
|
11
|
+
- User wants to include / eager-load relations.
|
|
12
|
+
- User wants to paginate, sort, filter, project.
|
|
13
|
+
- User wants to wrap operations in a transaction (`db.transaction(...)` — Postgres and SQLite).
|
|
14
|
+
- User wants to aggregate (`count`, `sum`, `avg`, …).
|
|
15
|
+
- User asks about query lanes (ORM vs SQL builder / query builder).
|
|
16
|
+
- User mentions: *query, select, where, orderBy, limit, offset, take, skip, include, eager load, first, all, count, aggregate, create, update, delete, upsert, returning, drizzle-style, kysely-style, prisma client*.
|
|
17
|
+
|
|
18
|
+
## When Not to Use
|
|
19
|
+
|
|
20
|
+
- User wants to add / change a model → `references/contract.md`.
|
|
21
|
+
- User wants to wire `db.ts` or add middleware → `references/runtime.md`.
|
|
22
|
+
- User is querying through a Supabase role-bound db (`asUser` / `asAnon` / `asServiceRole`, RLS, `auth.*` admin reads) → `references/supabase.md` for the role-binding surface; everything in this skill then applies to the returned `RoleBoundDb`.
|
|
23
|
+
- User wants to debug a query failure (structured error envelope) → `references/debug.md`.
|
|
24
|
+
|
|
25
|
+
## Pick your target
|
|
26
|
+
|
|
27
|
+
Prisma 8 ships **two query lanes per target** on the same `db` value from `src/prisma/db.ts`. **Before writing queries, read `db.ts` and load the matching target guide:**
|
|
28
|
+
|
|
29
|
+
| Runtime import in `db.ts` | Load |
|
|
30
|
+
| --- | --- |
|
|
31
|
+
| `@internal/postgres/runtime` | [`queries-postgres.md`](./queries-postgres.md) — `db.orm.<ns>.<Model>` + `db.sql.<ns>.<table>` |
|
|
32
|
+
| `@internal/mongo/runtime` | [`queries-mongo.md`](./queries-mongo.md) — `db.orm.<root>` + `db.query.from(...)` |
|
|
33
|
+
| `@internal/extension-supabase/runtime` | [`queries-postgres.md`](./queries-postgres.md) — a Supabase `RoleBoundDb` is a Postgres surface (`db.orm.<ns>.<Model>` + `db.sql.<ns>.<table>`); bind a role first via `references/supabase.md` |
|
|
34
|
+
|
|
35
|
+
Both targets share the contract and connection on one `db` value. Reach for the ORM first; drop to the lower-level lane when the ORM can't express the shape. Lane choice is local — one query function picks one lane, not the whole app.
|
|
36
|
+
|
|
37
|
+
**Do not mix target examples.** Postgres uses PascalCase model roots (`db.orm.public.User`) and `db.sql.public.user`; Mongo uses lowercased plural roots (`db.orm.users`) and `db.query.from('users')`. There is no `db.sql` on Mongo and no `db.query` SQL-builder equivalent on Postgres.
|
|
38
|
+
|
|
39
|
+
## Namespace-aware accessors
|
|
40
|
+
|
|
41
|
+
On Postgres, models and tables are **always** addressed by namespace coordinate — the storage namespace is the Postgres schema, and a model declared outside any `namespace { }` block lands in `public`:
|
|
42
|
+
|
|
43
|
+
- **ORM**: `db.orm.<namespace>.<Model>` — e.g. `db.orm.public.User`, `db.orm.auth.User`
|
|
44
|
+
- **SQL builder**: `db.sql.<namespace>.<table>` — e.g. `db.sql.public.user`, `db.sql.auth.users`
|
|
45
|
+
|
|
46
|
+
There is no flat `db.orm.User` / `db.sql.user` on the Postgres façade: `db.sql` is one facet per storage namespace and nothing else, and `db.orm` is keyed the same way (`examples/prisma-8-demo` uses `db.orm.public.User` and `db.sql.public.user` throughout). SQLite has no schemas, so its façade exposes the single unbound namespace directly — `db.orm.User` and `db.sql.user` are the SQLite spellings (`examples/prisma-8-demo-sqlite`). Mongo is keyed by collection storage name (`db.orm.users`).
|
|
47
|
+
|
|
48
|
+
See [`queries-postgres.md` § Namespace-aware accessors](./queries-postgres.md#namespace-aware-accessors) for a worked example.
|
|
49
|
+
|
|
50
|
+
## Consuming the result: `await`, `.toArray()`, or `for await`
|
|
51
|
+
|
|
52
|
+
Critical to get right early — on **both Postgres and Mongo**, `.all()` returns an **`AsyncIterableResult<Row>`**, which is *both* a `PromiseLike<Row[]>` and an `AsyncIterable<Row>`. That means three consumption forms all work, and the canonical one is the shortest:
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
const users = await db.orm.public.User.select('id', 'email').all();
|
|
56
|
+
// ^? Row[] ← the Thenable resolves to a real array. This is the default idiom.
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
You do **not** need a `collect()` / `toArray()` helper — `await` is enough. Internally `await` invokes the result's `then(...)`, which buffers the rows into an array. Two equivalent alternatives exist for the cases where they read better:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// `.toArray()` returns a genuine `Promise<Row[]>`. Reach for it only when
|
|
63
|
+
// something needs a real `Promise` and not merely a thenable: a slot typed
|
|
64
|
+
// `Promise<Row[]>` (an `AsyncIterableResult` has only `then`, not `catch` /
|
|
65
|
+
// `finally`, so it does not satisfy that annotation), or a runtime
|
|
66
|
+
// `instanceof Promise` check. Note that `await` and the `Promise.all` /
|
|
67
|
+
// `Promise.race` combinators all accept the thenable directly — those are
|
|
68
|
+
// NOT reasons to call `.toArray()`. Whenever you are just going to await it
|
|
69
|
+
// here, use `await ...all()` and skip `.toArray()`.
|
|
70
|
+
const rows: Promise<User[]> = db.orm.public.User.select('id', 'email').all().toArray();
|
|
71
|
+
|
|
72
|
+
// Iterate — decode and handle rows one at a time. Whether the raw rows are
|
|
73
|
+
// also fetched incrementally depends on the façade; see *Streaming* below.
|
|
74
|
+
for await (const user of db.orm.public.User.select('id', 'email').all()) {
|
|
75
|
+
process(user);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Two single-row shortcuts also exist on the result, in addition to the collection-level `.first()` (which issues `LIMIT 1` on Postgres):
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
const user = await db.orm.public.User.where({ id }).all().first();
|
|
83
|
+
// ^? Row | null ← buffers, returns the first row or null. Issues no LIMIT.
|
|
84
|
+
const required = await db.orm.public.User.where({ id }).all().firstOrThrow();
|
|
85
|
+
// ^? Row ← buffers; throws `RUNTIME.NO_ROWS` if empty.
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
For genuine single-row reads, prefer the *collection*-level `.first()` (which adds `LIMIT 1` to the SQL on Postgres) over `.all().first()` (which fetches all rows and discards the rest). The result-level helpers are for cases where you already need the full result and want the first row without an extra round-trip.
|
|
89
|
+
|
|
90
|
+
**The result is single-consumption.** Each `AsyncIterableResult` instance can be consumed once — by `await`, by `.toArray()`, or by `for await`. Trying to consume it a second time throws **`RUNTIME.ITERATOR_CONSUMED`**. The fix is almost always to store the array in a variable on first consumption and reuse the variable:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
// Bad — second await throws RUNTIME.ITERATOR_CONSUMED.
|
|
94
|
+
const result = db.orm.public.User.select('id', 'email').all();
|
|
95
|
+
const a = await result;
|
|
96
|
+
const b = await result;
|
|
97
|
+
|
|
98
|
+
// Good — buffer once, reuse the array.
|
|
99
|
+
const users = await db.orm.public.User.select('id', 'email').all();
|
|
100
|
+
const a = users;
|
|
101
|
+
const b = users;
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
If you've seen `collect(...)` / `toArray(...)` helpers in a codebase wrapping `.all()`, they're vestigial — `await` does the same thing for free. Remove them when you touch the surrounding code.
|
|
105
|
+
|
|
106
|
+
## Running queries from a short script
|
|
107
|
+
|
|
108
|
+
When the user is running a one-off `tsx my-script.ts` (not a long-lived server), call `await db.close()` at the end so the process exits cleanly — on Postgres the façade-owned pool keeps Node's event loop alive; on Mongo the façade-owned `MongoClient` does the same. See `references/runtime.md` § *Running as a script (teardown)* for the full pattern including `await using`.
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// src/scripts/seed.ts
|
|
112
|
+
import { db } from '../prisma/db';
|
|
113
|
+
|
|
114
|
+
// Postgres — PascalCase model root from contract
|
|
115
|
+
for (const u of users) {
|
|
116
|
+
await db.orm.public.User.create(u);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Mongo — lowercased plural root from contract (e.g. users, not User)
|
|
120
|
+
// for (const u of users) {
|
|
121
|
+
// await db.orm.users.create(u);
|
|
122
|
+
// }
|
|
123
|
+
|
|
124
|
+
console.log('Seeded.');
|
|
125
|
+
await db.close();
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Streaming
|
|
129
|
+
|
|
130
|
+
Every read terminal (`.all()`, and `runtime.query(plan)` for a SQL-builder plan) returns an `AsyncIterableResult`, so `for await` is always available. What it buys you depends on the façade:
|
|
131
|
+
|
|
132
|
+
- **Long-lived `postgres()` façade** (the usual `db.ts`): the driver runs with cursors disabled. The full result set is fetched from the server before the first row is yielded; only *decoding* happens per row. `for await` therefore does not bound the memory held by the raw result. For very large sets, paginate (`.limit()` / `.offset()`, or `.orderBy(...).cursor(...)`) instead.
|
|
133
|
+
- **Serverless façade** (`@prisma/orm-postgres/serverless`, one `connect()` per invocation): the driver reads through a server-side cursor in batches of 100 rows by default (`cursor: { batchSize }` on the façade options), so `for await` really does stream.
|
|
134
|
+
|
|
135
|
+
There is no `.stream()` method on either façade.
|
|
136
|
+
|
|
137
|
+
## Prepared statements (Postgres, SQLite)
|
|
138
|
+
|
|
139
|
+
`db.prepare(declaration, (sql, params) => plan)` builds a statement once and binds it per call. The declaration names each parameter's codec (`{ email: 'pg/text@1' }`); the callback receives the façade's `sql` builder plus typed `params` and returns a plan. A row-returning plan gives a `PreparedStatement` you run with `ps.query(runtime, params)`; a plan whose result is an affected count gives a `PreparedExecution` you run with `ps.execute(runtime, params)`. Declaring a parameter the plan never references throws `RUNTIME.PREPARE_UNUSED_PARAM`. `runtime.prepare(declaration, (params) => plan)` is the same thing on a `Runtime`.
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
// examples/prisma-8-demo/src/queries/get-user-by-email-prepared.ts
|
|
143
|
+
const ps = await db.prepare({ email: 'pg/text@1' }, (sql, params) =>
|
|
144
|
+
sql.public.user
|
|
145
|
+
.select('id', 'email', 'displayName', 'createdAt', 'kind')
|
|
146
|
+
.where((f, fns) => fns.eq(f.email, params.email))
|
|
147
|
+
.limit(1)
|
|
148
|
+
.build(),
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const runtime = db.runtime();
|
|
152
|
+
for (const email of emails) {
|
|
153
|
+
const rows = await ps.query(runtime, { email });
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Naming model and result types
|
|
158
|
+
|
|
159
|
+
The model is the whole row plus its relations, and each related model carries its own relations in turn, so no query returns a value of the model type. A query returns the fields it fetched. The default fetch returns `Scalars<Model>`, the model without relations: `db.orm.public.User.first()` returns `Scalars<Model> | null`, and `db.orm.public.User.all()` returns `Scalars<Model>[]` (or its async iterable). Four types cover every case, and none needs a client in scope:
|
|
160
|
+
|
|
161
|
+
- `Models.<ns>_<Model>` (from `contract.d.ts`) — every scalar field and every relation. On SQLite, which has no schemas, the name is bare: `Models.User` and `typeof models.User`. On Postgres the schema is part of the name: `Models.public_User`, or `typeof models.public.User` by dotted access — a model declared outside any `namespace { }` block is in `public`, so that is also its name. Mongo names its models the same way. A polymorphic base also emits one member per variant and an `Any<Base>` union (`Models.public_AnyTask`).
|
|
162
|
+
- `Scalars<M>` — the model without relations; what a default fetch returns. Distributes over unions, so `Scalars<Models.public_AnyTask>` is the union of variant rows.
|
|
163
|
+
- `Shape<M, Spec>` — a data structure derived from the model, for declaring an endpoint's response type once and having the compiler check the body at the `return`. At every level of `Spec`: `'+'` is a union of scalar and relation names to keep (a relation named there comes with all of its scalars and none of its relations; the scalars are narrowed only when `'+'` names a scalar, so `'+': 'posts'` alone is every scalar plus posts); `'-'` is a union of scalar names to drop; `'+'` naming a scalar beside `'-'` is a compile error, while `{ '-': 'passwordHash'; '+': 'posts' }` is every scalar but the hash plus posts; any other key is a relation whose value is a nested spec that narrows the related model. Relations are absent unless asked for; `X[]`, `X | null`, or `X` comes from the model. Wrong names, a relation in `'-'`, a non-object relation value, and a relation both in `'+'` and as a key are compile errors. No `where`/`orderBy`/`limit`; compose extras with TypeScript (`Shape<M> & { postCount: number }`).
|
|
164
|
+
- `ResultType<typeof query>` — the row of any ORM collection value (plain, `.include()`, `.select()`, `.variant()`), and of SQL lane plans. Bind the query to a name first; `typeof` needs a value.
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
import type { models, Models } from './prisma/contract';
|
|
168
|
+
import type { Scalars, Shape } from '@prisma/orm-postgres/family-contract/types';
|
|
169
|
+
import type { ResultType } from '@prisma/orm-postgres/components/runtime';
|
|
170
|
+
|
|
171
|
+
type User = typeof models.public.User; // same type as Models.public_User
|
|
172
|
+
type UserRow = ResultType<typeof db.orm.public.User>; // Scalars<Models.public_User>
|
|
173
|
+
|
|
174
|
+
const usersWithTasks = () => db.orm.public.User.include('tasks');
|
|
175
|
+
type UserWithTasks = ResultType<ReturnType<typeof usersWithTasks>>; // Shape<Models.public_User, { '+': 'tasks' }>
|
|
176
|
+
|
|
177
|
+
const projected = db.orm.public.User.select('id');
|
|
178
|
+
type UserId = ResultType<typeof projected>; // { id: number }
|
|
179
|
+
|
|
180
|
+
// An endpoint declares its response from the model; the query behind it is an implementation detail.
|
|
181
|
+
type UserResponse = Shape<Models.public_User, { '-': 'email'; posts: { '+': 'id' | 'title' | 'tags' } }>;
|
|
182
|
+
|
|
183
|
+
async function getUserWithPosts(userId: Models.public_User['id']): Promise<UserResponse | null> {
|
|
184
|
+
const user = await db.orm.public.User.where({ id: userId })
|
|
185
|
+
.include('posts', (posts) => posts.include('tags'))
|
|
186
|
+
.first();
|
|
187
|
+
if (user === null) return null;
|
|
188
|
+
const { email: _email, ...rest } = user;
|
|
189
|
+
return { ...rest, posts: user.posts.map(({ id, title, tags }) => ({ id, title, tags })) };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// @ts-expect-error 'nope' is not a relation of User
|
|
193
|
+
type Bad = Shape<Models.public_User, { nope: {} }>;
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
On Mongo the imports are `@prisma/orm-mongo/family-contract/types` and `@prisma/orm-mongo/components/runtime`; embedded documents are fields, so they stay in `Scalars`. To name a model plus some of its relations, write `Shape<Models.public_User, { '+': 'id' | 'posts' }>`, not `Pick<Models.public_User, 'id' | 'posts'>`: `Pick` demands fully loaded nested posts that no query returns. Input types (`CreateInput<Contract, 'User'>`, `MutationUpdateInput<Contract, 'User'>`, `ShorthandWhereFilter<Contract, 'public', 'User'>`) come from `@prisma/orm-postgres/orm-client`.
|
|
197
|
+
|
|
198
|
+
Coming from Prisma 7: `Prisma.User` → `Models.public_User` (note: now carries relations; the scalars-only row is `Scalars<Models.public_User>`); `Prisma.UserGetPayload<{ include: { posts: true } }>` → `Shape<Models.public_User, { '+': 'posts' }>`; `Prisma.UserGetPayload<{ select: { id: true; posts: { select: { title: true } } } }>` → `Shape<Models.public_User, { '+': 'id'; posts: { '+': 'title' } }>`; `Prisma.UserCreateInput` → `CreateInput<Contract, 'User'>`; `Awaited<ReturnType<typeof fn>>` → `ResultType<typeof query>`.
|
|
199
|
+
|
|
200
|
+
## Common Pitfalls (cross-target)
|
|
201
|
+
|
|
202
|
+
1. **Using Postgres examples on a Mongo project (or vice versa).** Check `db.ts` and load the correct target guide ([`queries-postgres.md`](./queries-postgres.md) or [`queries-mongo.md`](./queries-mongo.md)).
|
|
203
|
+
2. **Writing a `collect()` / `toArray()` helper to convert `.all()` to an array.** `.all()` returns an `AsyncIterableResult<Row>` which *is* a `PromiseLike<Row[]>` — `await collection.all()` directly yields `Row[]`. See *Consuming the result* above.
|
|
204
|
+
3. **Consuming an `AsyncIterableResult` twice.** Each result is single-use. The second consumer throws `RUNTIME.ITERATOR_CONSUMED`. Buffer once into a variable and reuse the variable.
|
|
205
|
+
|
|
206
|
+
Target-specific pitfalls live in the per-target guides.
|
|
207
|
+
|
|
208
|
+
## What Prisma 8 doesn't do yet
|
|
209
|
+
|
|
210
|
+
- **Many-to-many relations work through the junction.** `.include('tags', (tag) => tag.select(...))` traverses an N:M relation's `through` table, and nested `create` / `connect` / `disconnect` on an N:M relation write the junction rows for you (`examples/prisma-8-demo/src/orm-client/get-post-tags.ts`, `create-post-with-tags.ts`). The one refusal: a junction with required payload columns the relation API cannot populate throws `ORM.RELATION_MUTATION_UNSUPPORTED` — write that junction directly or use the SQL builder.
|
|
211
|
+
- **Ordering grouped aggregates by an aggregate alias (Postgres).** `db.orm.<ns>.<Model>.groupBy(...)` supports `.orderBy(...)` on group keys plus `.limit(...)` / `.offset(...)`, but the grouped collection cannot order by an aggregate alias such as `SUM(amount)`. A "top-N groups by SUM" query therefore falls back to JS-side sort + slice over the full grouped result, which is fine at small cardinalities and bad at scale. Workarounds: (a) drop to `db.sql.<ns>.<table>` and write the `GROUP BY` + `ORDER BY` + `LIMIT` against the aggregated table directly; (b) live with the JS-side sort/slice if the grouped cardinality is bounded. File a feature request via `references/feedback.md` if this is hitting you in production.
|
|
212
|
+
- **A raw-SQL lane.** This one exists. Write whole-query raw SQL through the client's raw lane: ``db.raw.sql`SELECT ...`.returnsRow({ ... }).build()`` for rows, or `.affectedCount()` for a mutation's row count. Each declared column names the codec that decodes it, so the row stays typed. For an expression fragment inside a builder query, use `fns.raw` in a `.select(...)` callback instead.
|
|
213
|
+
- **TypedSQL (`.sql` files compiled into typed callables).** Not implemented. For a repeated query, use `db.prepare(...)` (see *Prepared statements* above) or a function that returns the built plan and `db.runtime().query(plan)` at the call site. If you want a `.sql`-file compile path, file a feature request via `references/feedback.md`.
|
|
214
|
+
- **`EXPLAIN` / query-plan inspection.** Prisma 8 does not expose an `.explain()` method. Workaround: connect a `pg.Pool` you control via the runtime's `pg:` binding (see `references/runtime.md`) and issue `EXPLAIN ANALYZE` through it. If you want a first-class plan-inspection surface, file a feature request via `references/feedback.md`.
|
|
215
|
+
- **Cursor-backed streaming on the long-lived façade.** `for await` works everywhere, but on `postgres()` the raw result is fetched in full before iteration (see *Streaming* above); only the serverless façade reads through a cursor. Paginate for very large sets on the long-lived façade. If you want cursor streaming there, file a feature request via `references/feedback.md`.
|
|
216
|
+
- **Multi-statement batching (Prisma-7-style `db.$transaction([call1, call2])`).** Prisma 8 runs each call sequentially. Workaround: wrap atomically-related work in `db.transaction(async (tx) => { ... })` on Postgres. If you want batch-as-array semantics, file a feature request via `references/feedback.md`.
|
|
217
|
+
- **Mongo façade transactions.** `@internal/mongo/runtime` does not expose `db.transaction(...)`. Multi-document atomicity is not yet wrapped in the Prisma 8 Mongo façade. Workaround: use the MongoDB driver's session API directly if you control the client binding (`mongoClient:` option). File a feature request via `references/feedback.md` if you need a first-class façade surface.
|
|
218
|
+
- **Mongo ORM aggregates.** No `.aggregate(...)` / `.groupBy(...)` on `db.orm.<root>`. Workaround: express aggregations through `db.query.from(...).group(...).build()` and `runtime.query(plan)`.
|
|
219
|
+
- **Mongo filter helpers on the façade.** Rich filters (`.in`, ranges, boolean composition) currently import from `@prisma/orm-mongo/query-ast/execution` (`MongoFieldFilter`, etc.) — not re-exported on `@internal/mongo/runtime`. Workaround: use object equality `.where({ field: value })` where possible; import from the internal package only when necessary. Tracked alongside façade-completeness gaps in Linear `TML-2526`.
|
|
220
|
+
- **Automatic N+1 detection.** Prisma 8 does not warn when an `.include(...)` is missing. Workaround: be deliberate about `.include(...)` in code review; the `lints` middleware (see `references/runtime.md`) catches the more common authoring slips (missing `WHERE` on a `DELETE` / `UPDATE`, missing `LIMIT` on a `SELECT`).
|
|
221
|
+
|
|
222
|
+
## Reference Files
|
|
223
|
+
|
|
224
|
+
This skill is split for selective loading. Target-specific reference paths live in the per-target guides:
|
|
225
|
+
|
|
226
|
+
- **Postgres** — [`queries-postgres.md` § Reference Files](./queries-postgres.md#reference-files)
|
|
227
|
+
- **Mongo** — [`queries-mongo.md` § Reference Files](./queries-mongo.md#reference-files)
|
|
228
|
+
|
|
229
|
+
## Checklist
|
|
230
|
+
|
|
231
|
+
- [ ] Confirmed the active target from `db.ts` and loaded the matching guide ([`queries-postgres.md`](./queries-postgres.md) or [`queries-mongo.md`](./queries-mongo.md)).
|
|
232
|
+
- [ ] On Postgres, used `db.orm.<ns>.<Model>` / `db.sql.<ns>.<table>` coordinates (usually `public`) — not a flat `db.orm.User`, which exists only on SQLite.
|
|
233
|
+
- [ ] Chose the right lane (ORM by default; lower-level builder for shapes the ORM doesn't express).
|
|
234
|
+
- [ ] Used `.first()` / `.first({ pk })` (Postgres) or `.where({ ... }).first()` (Mongo) for single-row reads — not `.all()`.
|
|
235
|
+
- [ ] Consumed `.all()` with plain `await` (not a `collect()` / `toArray()` helper). Used `for await` only when per-row handling is actually wanted — and did not promise it bounds memory on the long-lived façade — and never iterated the same result twice.
|
|
236
|
+
- [ ] Did NOT use `db.sql` on a Mongo project or `db.query` where the Postgres SQL builder is meant.
|
|
237
|
+
- [ ] Completed the target-specific checklist in the loaded guide.
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
|
|
2
|
+
# Prisma 8 — Quickstart (Adoption)
|
|
3
|
+
|
|
4
|
+
> **Edit your data contract. Prisma handles the rest.**
|
|
5
|
+
|
|
6
|
+
This skill takes the user from zero (or near-zero) to a first working query against Prisma 8. Three paths — and they all converge on the same first arc: **connect → write → read**. Schema editing comes *after* the first arc, not before.
|
|
7
|
+
|
|
8
|
+
- **First-touch orientation** — the user has arrived at a Prisma 8 project for the first time (a scaffold tool like `npx createprisma` dropped them in, they cloned a teammate's repo, or they ran `prisma orm init` themselves and now want to make their first move) and they're asking *"what can I do with Prisma 8?"*, *"where do I start?"*, or *"what's next?"*. The goal is to anchor them on the contract, get them connected to a database, round-trip one row, and let further commands surface organically.
|
|
9
|
+
- **Greenfield** — new project, fresh database. User runs `prisma orm init` themselves. `init` seeds a starter contract with a sample model, so the path joins the first-touch orientation arc as soon as the database is initialised.
|
|
10
|
+
- **Brownfield-DB** — existing database, no contract yet. Infer the contract from the database with `contract infer`, sign the marker with `db sign`, then write queries against one of the existing tables.
|
|
11
|
+
|
|
12
|
+
This skill does **not** cover migrating from another ORM (Drizzle, Prisma 6/7, Sequelize, TypeORM, Kysely, Knex, raw drivers). Those are separately-installable skills.
|
|
13
|
+
|
|
14
|
+
## When to Use
|
|
15
|
+
|
|
16
|
+
- User asks *"what can I do with Prisma 8?"*, *"what can I do next with Prisma?"*, *"where do I start?"*, *"what should I do first?"* — and a PN project already exists on disk. **First-touch orientation** path below.
|
|
17
|
+
- User just ran `createprisma` (or equivalent scaffold tool) and is asking what to do next. **First-touch orientation** path.
|
|
18
|
+
- User is starting a new project and wants to use Prisma 8. **Greenfield** path.
|
|
19
|
+
- User has an existing database (no PN contract) and wants to introduce PN. **Brownfield-DB** path.
|
|
20
|
+
- User typed *"prisma orm init"*, *"get started with PN"*, *"set up PN"*, *"how do I scaffold a project"*. **Greenfield** path.
|
|
21
|
+
- User says *"I have an existing Postgres/Mongo, how do I start using PN?"*. **Brownfield-DB** path.
|
|
22
|
+
|
|
23
|
+
## When Not to Use
|
|
24
|
+
|
|
25
|
+
- User already has a PN project and wants to add a model → `references/contract.md`.
|
|
26
|
+
- User wants to migrate FROM a specific ORM → install `@internal/migrate-from-<orm>-skill` (separate).
|
|
27
|
+
- User wants to wire `db.ts` in a project that already has a contract → `references/runtime.md`.
|
|
28
|
+
- User wants to integrate Prisma 8 with a build tool (Vite plugin, Next.js, …) → `references/build.md`.
|
|
29
|
+
|
|
30
|
+
## Key Concepts
|
|
31
|
+
|
|
32
|
+
- **Contract**: the data model. Authored as `contract.prisma` (PSL, the canonical surface) or `contract.ts` (TypeScript builder). The framework reads it and emits two artefacts: `contract.json` (runtime IR) and `contract.d.ts` (types).
|
|
33
|
+
- **Target**: the backing store. Today: `postgres` or `mongodb`. Picked at `init` time; baked into the `@internal/<target>` façade the scaffold imports from.
|
|
34
|
+
- **Authoring mode**: how you write the contract. `psl` (Prisma Schema Language, default) or `typescript` (programmatic builder, optionally paired with the Vite plugin for auto-emit during `vite dev` — see `references/build.md`).
|
|
35
|
+
- **Façade packages.** The scaffold installs exactly one façade per target — `@internal/postgres` (or `@internal/mongo`). User code imports from façade subpaths (`@internal/postgres/config`, `@internal/postgres/runtime`, `@internal/postgres/contract-builder`). The façade bakes in the family / target / adapter / driver wiring; never reach past it. See `references/contract.md` for the full list.
|
|
36
|
+
- **`db.ts`**: the runtime entry point. Lives next to the contract source at `src/prisma/db.ts`. Imports the contract artefacts and exports a `db` value the rest of the app uses.
|
|
37
|
+
- **Marker**: a row in the `prisma_contract.marker` table (Postgres) or a document in the `_prisma_migrations` collection (Mongo) that records the contract hash per contract space. Lets PN detect drift between contract and live DB. Created by `db init` (greenfield / first-touch orientation) or `db sign` (brownfield).
|
|
38
|
+
|
|
39
|
+
### Canonical on-disk layout
|
|
40
|
+
|
|
41
|
+
Every application that consumes Prisma 8 uses the same shape:
|
|
42
|
+
|
|
43
|
+
```text
|
|
44
|
+
<app-root>/
|
|
45
|
+
├── prisma.config.ts ← project config at repo root
|
|
46
|
+
├── src/
|
|
47
|
+
│ └── prisma/
|
|
48
|
+
│ ├── contract.prisma ← (or contract.ts) — schema source you author
|
|
49
|
+
│ ├── contract.json ← emitted by `contract emit` — do not edit
|
|
50
|
+
│ ├── contract.d.ts ← emitted by `contract emit` — do not edit
|
|
51
|
+
│ └── db.ts ← runtime entry; the rest of `src/` imports from here
|
|
52
|
+
└── migrations/
|
|
53
|
+
├── snapshots/ ← content-addressed contract store, shared across spaces
|
|
54
|
+
│ └── <hex>/
|
|
55
|
+
│ ├── contract.json
|
|
56
|
+
│ └── contract.d.ts
|
|
57
|
+
└── app/ ← created on first `migration plan` / `db init`
|
|
58
|
+
├── refs/db.json
|
|
59
|
+
└── <timestamp>_<slug>/
|
|
60
|
+
├── migration.json
|
|
61
|
+
├── ops.json
|
|
62
|
+
└── migration.ts
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Three things to internalise:
|
|
66
|
+
|
|
67
|
+
- **`src/prisma/` is the home for the contract** — source + emitted artefacts + `db.ts` all colocated. The rest of `src/` imports from `./prisma/db` (or `../prisma/db`, depending on file depth).
|
|
68
|
+
- **`migrations/app/`** — the `app/` segment is the consuming application's space-id. Extensions you depend on get sibling directories under `migrations/` (one per extension contract-space), but you don't write into those — only the `app/` subtree is your migrations.
|
|
69
|
+
- **`prisma.config.ts` lives at the repo root**, not under `src/`. Every command resolves paths relative to the config's directory.
|
|
70
|
+
|
|
71
|
+
**Contributors building extension packages or aggregate-root monorepo packages use a different layout** — `src/contract.{prisma,ts}` (no `prisma/` subdir) + `migrations/<timestamp>_<slug>/` (no `app/` segment). That distinction is intentional; see `references/contract.md` for which path applies to you.
|
|
72
|
+
|
|
73
|
+
`prisma orm init` scaffolds this layout by default: the contract at `src/prisma/contract.prisma` (or `.ts`) and `db.ts` beside it. A project scaffolded by an older init may have a top-level `prisma/` directory instead — read the `contract` path in `prisma.config.ts` rather than assuming either.
|
|
74
|
+
|
|
75
|
+
## Your first arc — connect, write, read
|
|
76
|
+
|
|
77
|
+
All three paths in this skill converge here. Once the project is scaffolded and the database is reachable, the first move is **always** the same: connect, write a row, read it back, against whatever model the contract already declares. Don't touch the contract source on this first move — extend it later, after the round-trip works.
|
|
78
|
+
|
|
79
|
+
Write the snippet in a fresh file directly under `src/` (e.g. `src/first-arc.ts`) so the relative import resolves to one level deep:
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// src/first-arc.ts
|
|
83
|
+
import 'dotenv/config';
|
|
84
|
+
import { db } from './prisma/db';
|
|
85
|
+
|
|
86
|
+
// Write a row against the starter model. Adapt the field names to whatever
|
|
87
|
+
// model your contract source actually declares — read it first.
|
|
88
|
+
await db.orm.public.User.create({ email: 'alice@example.com' });
|
|
89
|
+
|
|
90
|
+
// Read it back.
|
|
91
|
+
const users = await db.orm.public.User.select('id', 'email').all();
|
|
92
|
+
console.log(users);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
If that prints `[{ id: 1, email: 'alice@example.com' }]`, the project is wired end-to-end and the user has crossed from *"I have a project"* to *"I'm building."*
|
|
96
|
+
|
|
97
|
+
`db.orm.<ns>.<Model>` is the default ORM lane — model-shaped, fully typed against the contract, lazily connects to the database on first use (it picks up `DATABASE_URL` from `.env` via the runtime's `dotenv/config`-loaded environment). The deeper `references/queries.md` reference covers the rest of the supported surface (filters, joins, transactions, the SQL builder, raw SQL via `db.raw.sql`, prepared statements) when the user is ready — and names the gaps (TypedSQL is not available).
|
|
98
|
+
|
|
99
|
+
> **SQLite target:** `prisma orm init` scaffolds only `postgres` and `mongodb`; a SQLite project is wired by hand with the `@internal/sqlite` façade (`references/runtime.md` § *Switch between Postgres, SQLite, and Mongo*; `examples/prisma-8-demo-sqlite`). SQLite has no schemas, so that façade exposes the unbound namespace directly — write `db.orm.User` rather than `db.orm.public.User`.
|
|
100
|
+
>
|
|
101
|
+
> **Mongo target:** the snippet above is SQL-target shape. On `@internal/mongo`, `db.orm` is keyed by the collection's storage name (`@@map(...)`, or the lowercased model name if no `@@map`), so the same arc reads `await db.orm.users.create(...)` / `await db.orm.users.select('id', 'email').all()` — not `db.orm.public.User`. Full rule and rewrite recipe in `references/queries.md` § *MongoDB ORM addressing*.
|
|
102
|
+
|
|
103
|
+
**Prerequisites for the arc to work.** All three paths leave these in place by the time you reach the arc:
|
|
104
|
+
|
|
105
|
+
- `prisma.config.ts` exists at the repo root and declares the target + contract source (typically `src/prisma/contract.prisma` or `src/prisma/contract.ts`).
|
|
106
|
+
- The contract source exists at `src/prisma/contract.{prisma,ts}` (a starter model from `init`, or the inferred contract from `contract infer`, or whatever the bootstrap tool generated).
|
|
107
|
+
- `src/prisma/db.ts` exists and instantiates the runtime with the emitted contract.
|
|
108
|
+
- `DATABASE_URL` is set in `.env` (or wherever the runtime's config tells it to look).
|
|
109
|
+
- The database has been initialised (`db init`) or marker-signed (`db sign`), so the marker row exists and the schema matches the contract.
|
|
110
|
+
|
|
111
|
+
The three workflows below each describe how their path gets the user to that state. After that, the arc above is the same.
|
|
112
|
+
|
|
113
|
+
## Workflow — First-touch orientation
|
|
114
|
+
|
|
115
|
+
Triggers: *"what can I do with Prisma 8?"*, *"what can I do next with Prisma?"*, *"where do I start?"*, *"I just ran createprisma"*, *"what's next?"*, or any close variant — paired with a PN project already on disk (scaffolded by `createprisma`, by `prisma orm init`, by a teammate, however).
|
|
116
|
+
|
|
117
|
+
The user's high-level intent is *"I want to be running an application against my database, against this thing called Prisma 8."* The job of this workflow is to anchor them on the contract, get one round-trip working, and let further commands surface organically as their next move requires them. **It is orientation, not a tour, not a feature inventory, not a syllabus.**
|
|
118
|
+
|
|
119
|
+
### Concept — what to communicate first
|
|
120
|
+
|
|
121
|
+
Prisma 8 is contract-first. Everything the framework does — query types, migrations, runtime types, drift detection — flows from a single source of truth: the **contract**. The contract describes the user's application's data model. The framework reads it; the framework derives the rest. Lead with this.
|
|
122
|
+
|
|
123
|
+
The first response to *"what can I do with Prisma 8?"* names the contract path, frames its role in one sentence, and then steers toward getting the user's application running. Don't open with a feature inventory. Don't open with a list of commands. Open with: *"Your contract is at `<path>`. It describes your application — your query types, migrations, and runtime types all flow from it. Let's get you connected to a database so your app can actually run against it."*
|
|
124
|
+
|
|
125
|
+
The first **arc** — once oriented — is **connect → write → read**. Not edit-the-contract-first, not plan-a-migration-first. The user's win is *I have application code running against my database*.
|
|
126
|
+
|
|
127
|
+
### Step 1 — Read the project, name the contract
|
|
128
|
+
|
|
129
|
+
Before saying anything specific to the user, read:
|
|
130
|
+
|
|
131
|
+
- `prisma.config.ts` at the repo root — what target (`postgres` / `mongodb`) is wired, what `contract:` path it declares, what extensions are installed.
|
|
132
|
+
- The contract source the config declares (canonically `src/prisma/contract.prisma` or `src/prisma/contract.ts`; a project scaffolded by an older init may have it at `prisma/contract.{prisma,ts}` instead — check the `contract` field of the config) — what starter models, if any, exist.
|
|
133
|
+
- `src/prisma/db.ts` (next to the contract) — the runtime entry point.
|
|
134
|
+
- `.env` / `.env.example` — is `DATABASE_URL` set, or only the example?
|
|
135
|
+
- Optionally `pnpm prisma db verify` — does the live DB match the contract? (Exit `4` with findings means drift or no marker; `2` means it could not run.)
|
|
136
|
+
|
|
137
|
+
Then **say the contract path back to the user, with its role attached**. Something like: *"Your contract is at `src/prisma/contract.prisma`, and it currently declares a `User` model. The contract describes your app — every query type, migration, and runtime type the framework gives you flows from this file. Let's get your app connected to a database next."* The exact wording is up to the agent; what matters is that the user leaves the first response knowing *where the contract is* and *that it is the source of truth*.
|
|
138
|
+
|
|
139
|
+
### Step 2 — Get the user's app connected and round-tripping
|
|
140
|
+
|
|
141
|
+
The motivation is *"so your app can actually run against your database"*, not *"so the prerequisite checklist passes"*. The mechanics depend on what's already in place from Step 1:
|
|
142
|
+
|
|
143
|
+
- **Everything already wired.** Go straight to writing and reading a row (see *Your first arc — connect, write, read* above). Adapt the snippet to whatever model the contract declares.
|
|
144
|
+
- **`DATABASE_URL` not set.** Have the user set it in `.env` (not in `prisma.config.ts` — see Pitfall 5). Then `pnpm prisma db init` to apply the current contract to that database and write the marker row. Now the app can connect.
|
|
145
|
+
- **Database is connectable but not yet aware of the contract** (marker row missing; `db verify` reports drift). Run `pnpm prisma db init`. (`db update` is the alternative for quick dev cycles — it's looser, doesn't write a migration history, and is what users reach for when they want to iterate on the schema fast. Mention it if the user asks how to make schema changes flow to the DB; don't pre-explain it.)
|
|
146
|
+
- **Contract is empty** (bootstrap left the source blank). Add **one** model with **two** fields (e.g. `User { id, email }`), `pnpm prisma contract emit`, then `pnpm prisma db init`. Minimal — get the round-trip working, *then* extend.
|
|
147
|
+
|
|
148
|
+
The user encounters `db init` (and optionally `db update`, `contract emit`) here because they're the commands their current move *requires*. They learn what those commands are by using them.
|
|
149
|
+
|
|
150
|
+
### Step 3 — Round-trip a row
|
|
151
|
+
|
|
152
|
+
Run the snippet from *Your first arc — connect, write, read* above against whatever model the contract declares. When it prints the row back, the user has crossed from *"I have a project"* to *"my app runs against my database"*. That's the win.
|
|
153
|
+
|
|
154
|
+
### Step 4 — Hand off to the next move
|
|
155
|
+
|
|
156
|
+
Now ask the user what they want to build. Route to the skill that owns that move:
|
|
157
|
+
|
|
158
|
+
- More queries (filters, joins, transactions) → `references/queries.md`.
|
|
159
|
+
- Add a model, change a field, add a relation → `references/contract.md`. They'll touch `contract emit` and `db update` (or `migration plan` + `db migrate`) as part of that workflow.
|
|
160
|
+
- Middleware, environment config, multiple targets → `references/runtime.md`.
|
|
161
|
+
- Vite / Next.js / dev-server integration → `references/build.md`.
|
|
162
|
+
- They want a fuller toolbelt overview at this point — *Commands you'll use day-to-day* below is the one-glance summary.
|
|
163
|
+
|
|
164
|
+
### Anti-patterns on this path
|
|
165
|
+
|
|
166
|
+
- **Leading with a feature tour or capability inventory.** The user asked what they can *do*. Get them doing it.
|
|
167
|
+
- **Listing commands before any have been used.** Commands belong to specific moves; surface them when the move requires them.
|
|
168
|
+
- **Diving into migration concepts before one query has run.** Migrations exist; their value lands later.
|
|
169
|
+
- **Adding several models in one go.** Add one, get one query green, then iterate.
|
|
170
|
+
- **Walking the user through `prisma.config.ts` keys.** The scaffold's defaults are correct; revisit when the user needs to change something.
|
|
171
|
+
- **Skipping the contract framing.** Even one line — *"your contract is at `<path>`, it's the source of truth"* — anchors the user; without it, the rest of the workflow lands as disconnected ceremony.
|
|
172
|
+
|
|
173
|
+
## Workflow — Greenfield
|
|
174
|
+
|
|
175
|
+
The concept: `prisma orm init` is one CLI command that scaffolds config, schema, runtime, dependencies, and the contract emit step. It operates on the current working directory — there is no positional project-name argument. Make the directory, `cd` in, then run init.
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
mkdir my-app && cd my-app
|
|
179
|
+
pnpm init # if no package.json yet
|
|
180
|
+
pnpm dlx prisma@latest orm init # interactive
|
|
181
|
+
# or non-interactive (CI / agent runs):
|
|
182
|
+
pnpm dlx prisma@latest orm init --yes --target postgres --authoring psl
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
> **Telemetry is opt-out.** The CLI collects anonymous usage data by default. Every command — including `init` — prints a one-time notice to **stderr** on first use, then sends; there is no interactive consent prompt. Opt out anytime by running `prisma telemetry disable`, or with `DO_NOT_TRACK=1` or `PRISMA_DISABLE_TELEMETRY=1`. The command stores `"enableTelemetry": false` in your user config for you (the CLI's per-user config dir, **not** `prisma.config.ts`). Run `prisma telemetry status` to see what's currently in effect. This is relevant for agent-driven runs — the CLI records that an agent invoked it. What's collected, the per-user config path, and how to fully reset are documented in `docs/Telemetry.md`.
|
|
186
|
+
|
|
187
|
+
The flags `init` accepts (run `prisma orm init --help` for the source of truth):
|
|
188
|
+
|
|
189
|
+
- `--target <db>` — `postgres` or `mongodb`.
|
|
190
|
+
- `--authoring <style>` — `psl` or `typescript`.
|
|
191
|
+
- `--schema-path <path>` — where to write the starter schema. Defaults to `src/prisma/contract.prisma` (or `src/prisma/contract.ts` with `--authoring typescript`), the canonical layout above. The extension must agree with `--authoring` (`CLI.INIT_AUTHORING_SCHEMA_PATH_MISMATCH` otherwise).
|
|
192
|
+
- `--confirm <directory name>` — grant the reinit consent non-interactively. Re-running init in a scaffolded directory asks you to type the directory name back before it overwrites; non-interactive runs pass the name with this flag instead (`--yes` does not grant it).
|
|
193
|
+
- `--write-env` — also write `.env` (default writes only `.env.example`; `.env` stays under your control).
|
|
194
|
+
- `--probe-db` — connect to `DATABASE_URL` once and check the server version against the target's minimum.
|
|
195
|
+
- `--strict-probe` — fail init if the probe fails (errors without `--probe-db`).
|
|
196
|
+
- `--skip-install` — skip dependency install + initial contract emit.
|
|
197
|
+
- `--keep-previous-facade` — when re-running init to switch targets, keep the previous target package in `package.json`.
|
|
198
|
+
|
|
199
|
+
`init` does not install agent skills and has no `--skip-skills` flag: the `prisma-8` skill ships inside the `@prisma/orm-*` package the project installs, and the family-level `prisma init` / `prisma skills sync` commands copy it into the agent harness directories.
|
|
200
|
+
|
|
201
|
+
`init` writes (when it runs cleanly):
|
|
202
|
+
|
|
203
|
+
- `prisma.config.ts` at the project root (envelope form — see `references/contract.md`).
|
|
204
|
+
- The contract source at `--schema-path` (`src/prisma/contract.prisma` by default).
|
|
205
|
+
- `db.ts` in the same directory as the contract source.
|
|
206
|
+
- `prisma-8.md` — a human quick-reference.
|
|
207
|
+
- `.env.example` (and `.env` if `--write-env`).
|
|
208
|
+
- Updates `package.json` (deps + a `contract:emit` script) and `tsconfig.json` (required compiler options).
|
|
209
|
+
- Installs deps and runs `prisma contract emit` once. If the install or the emit fails, the scaffold is still on disk and init exits `4` (`CLI.INIT_INSTALL_FAILED`) or `5` (`CLI.INIT_EMIT_FAILED`) with the step to re-run.
|
|
210
|
+
|
|
211
|
+
After init succeeds, the path converges on *Your first arc — connect, write, read* above. `init` has already seeded a starter contract with `User` and `Post` models (with a relation between them) and run `contract emit` once; the only remaining prerequisites are setting `DATABASE_URL` and initialising the database. Two commands:
|
|
212
|
+
|
|
213
|
+
1. Set `DATABASE_URL` in `.env` (copy from `.env.example`).
|
|
214
|
+
2. Initialise the database: `pnpm prisma db init`. Creates tables, indexes, constraints, and writes the marker row — using the starter contract `init` generated.
|
|
215
|
+
|
|
216
|
+
Then run the snippet from *Your first arc* above against the `User` model. When the user is ready to extend the contract — add more models, change fields, add relations — chain to `references/contract.md`. For more queries, chain to `references/queries.md`.
|
|
217
|
+
|
|
218
|
+
**Why this is queries-first, not schema-editing-first.** `init` ships with `User` and `Post` on purpose: the user shouldn't have to design a schema to prove their setup works. Extending the contract is the next move *after* the first arc lands, not part of getting there. If the user asks you to skip straight to *"add a Comment model"* — sure, do that — but get one query green against `User` or `Post` first if there's any doubt the project is wired correctly.
|
|
219
|
+
|
|
220
|
+
## Workflow — Brownfield-DB (existing database, no contract)
|
|
221
|
+
|
|
222
|
+
The concept: against an existing database with no PN contract, `contract infer` walks the live schema (tables, columns, indexes — including expression and partial ones — constraints, and RLS enablement + policies) and writes a PSL contract that describes it. Where authoring would generate a CHECK constraint the database does not carry (the element-non-null check on a list column), infer emits `@noCheck(elementNotNull)` on that field, so the contract declares exactly what the database enforces. The reverse gap is closed too: a hand-written CHECK constraint the database enforces that authoring would never have generated comes back as `@@check(expression: <reprint>, map: "<name>")`, so it is a declared object from the first pull instead of an invisible extra a later destructive plan could drop. That inferred check warns (`PN_EXACT_NAME_BODY_COMPARISON`) the next time you run `contract emit` — expected, not a defect: the warning fires on any `map:` body regardless of who wrote it, and the comparison stays sound because both sides are Postgres's own reprint. The result is a *starting point*, not the final contract — review and clean it up, then `db sign` to record the current contract hash as the marker (instead of letting `db init` try to recreate the schema from scratch). By default `db sign` also sets the `db` ref and stores the contract snapshot, so the next `migration plan` chains from the adopted schema — even when the database is named with `--db`. `--advance-ref <name>` writes another ref instead and `--no-advance-ref` writes none; in either case the next plan needs `--from` or a `db` ref you set yourself.
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
mkdir my-app && cd my-app
|
|
226
|
+
pnpm init
|
|
227
|
+
pnpm dlx prisma@latest orm init --yes --target postgres --authoring psl
|
|
228
|
+
# scaffold lands at src/prisma/; you'll overwrite the starter schema below
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Then, with `DATABASE_URL` set in `.env`:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
pnpm prisma contract infer --db "$DATABASE_URL" --output src/prisma/contract.prisma
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
(Note: the flag is `--output`, not `--out`. Run `prisma contract infer --help` for the full surface.)
|
|
238
|
+
|
|
239
|
+
The agent should pause here and read the inferred PSL. Symptoms a re-author pass is needed:
|
|
240
|
+
|
|
241
|
+
- Tables PN couldn't categorise (e.g. legacy linking tables you could express as relations).
|
|
242
|
+
- Columns where PN's type guess is wrong (e.g. `String` where you want an extension type like `pgvector.Vector(length: 1536)`).
|
|
243
|
+
- Missing `@unique` / `@index` hints PN couldn't see.
|
|
244
|
+
- Field names you'd prefer to alias.
|
|
245
|
+
|
|
246
|
+
Then re-emit and sign:
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
pnpm prisma contract emit
|
|
250
|
+
pnpm prisma db sign # writes the marker and sets the db ref
|
|
251
|
+
pnpm prisma db verify # exit 0 immediately after a pull; exit 4 with findings if the DB drifts later
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Then run the snippet from *Your first arc — connect, write, read* above, using one of your existing tables in place of the starter model. The arc is the same; only the path that got you there differs.
|
|
255
|
+
|
|
256
|
+
## Commands you'll use day-to-day
|
|
257
|
+
|
|
258
|
+
A reference table — not a script to recite at the user. Commands surface in the workflow above as the user's next move requires them; this table is here for the moment the user asks for a wider view (typically after the first round-trip), and as a one-glance summary anyone newly oriented to Prisma 8 can scan. For flag-level detail, run `<command> --help`; the help output is the source of truth.
|
|
259
|
+
|
|
260
|
+
| What you want to do | Command | Deeper skill |
|
|
261
|
+
|---|---|---|
|
|
262
|
+
| Apply the current contract to the DB the first time | `prisma db init` | this skill |
|
|
263
|
+
| Re-emit `contract.json` + `contract.d.ts` after editing the contract source | `prisma contract emit` | `references/contract.md` |
|
|
264
|
+
| Quick dev-only schema sync (no migration history kept) | `prisma db update` | `references/migrations.md` |
|
|
265
|
+
| Plan a migration from a contract diff | `prisma migration plan --name <slug>` | `references/migrations.md` |
|
|
266
|
+
| Apply pending migrations | `prisma db migrate` | `references/migrations.md` |
|
|
267
|
+
| Inspect the live database | `prisma db schema` | `references/debug.md` |
|
|
268
|
+
| Confirm the DB matches the contract (drift check) | `prisma db verify` | `references/debug.md` |
|
|
269
|
+
| Bring an existing DB into a PN contract | `prisma contract infer --db "$DATABASE_URL"` | this skill (brownfield) |
|
|
270
|
+
| Decode a structured error envelope | (read the `code` / `why` / `fix` fields) | `references/debug.md` |
|
|
271
|
+
| Report a bug or request a feature | (file via the feedback skill) | `references/feedback.md` |
|
|
272
|
+
|
|
273
|
+
## Decision — PSL vs TypeScript authoring
|
|
274
|
+
|
|
275
|
+
- **PSL** (`contract.prisma`) — the default. Concise, declarative, familiar to anyone who has used Prisma. Recommended for most projects.
|
|
276
|
+
- **TypeScript** (`contract.ts`) — a programmatic builder. Use when the contract is genuinely computed (multi-tenant per-tenant variants), when you reuse contract fragments across files, or when an extension requires constructs PSL doesn't yet express (e.g. pgvector's parameterised storage-type registration). Pairs with the Vite plugin from `references/build.md` for auto-emit on save.
|
|
277
|
+
|
|
278
|
+
Switch authoring later by re-running `prisma orm init` in the same directory. The init flow detects the existing scaffold and prompts to reinit (non-interactive runs grant the consent with `--confirm <directory name>`). Existing contract content is *not* automatically translated — you'll re-author by hand in the target language.
|
|
279
|
+
|
|
280
|
+
## Common Pitfalls
|
|
281
|
+
|
|
282
|
+
1. **Running `prisma orm init <project-name>` with a positional argument.** `init` operates on the current working directory; there is no positional project-name argument. `mkdir foo && cd foo && pnpm dlx prisma@latest orm init`.
|
|
283
|
+
2. **`init` doesn't connect to your database.** It only scaffolds files and installs dependencies (and runs the initial `contract emit`). You connect with `db init` / `db update` / `db migrate`. If `init` succeeds and queries fail, the issue is `DATABASE_URL`, not `init`.
|
|
284
|
+
3. **Treating inferred PSL as the final contract.** `contract infer` produces a starting point. Don't `db sign` against a contract you haven't read.
|
|
285
|
+
4. **Forgetting to emit after editing the contract.** The contract artefacts (`contract.json`, `contract.d.ts`) are stale until you run `contract emit`. If the type-checker says a model "doesn't exist", you skipped emit.
|
|
286
|
+
5. **Setting `DATABASE_URL` in `prisma.config.ts` instead of `.env`.** Nothing reads `.env` on its own: the scaffolded `prisma.config.ts` starts with `import 'dotenv/config'`, and that import is what loads `.env` into `process.env` before the config (and the CLI running it) reads `process.env['DATABASE_URL']`. Keep the import; a config without it sees no `.env` values. Hardcoding the URL leaks credentials and bypasses per-environment overrides. See `references/runtime.md`.
|
|
287
|
+
6. **Hand-editing `contract.json` or `contract.d.ts`.** They're emitted artefacts; the next `contract emit` overwrites your changes. Edit the source instead.
|
|
288
|
+
7. **Using `--out` for `contract infer`.** The flag is `--output`.
|
|
289
|
+
|
|
290
|
+
## What Prisma 8 doesn't do yet
|
|
291
|
+
|
|
292
|
+
- **Migration from another ORM.** Prisma 8 doesn't migrate your schema *from* Drizzle / Prisma 6/7 / Sequelize / TypeORM / Kysely / Knex / a raw driver. Workaround: install the matching `@internal/migrate-from-<orm>-skill` if one exists for your source, or treat the source as a brownfield database and `contract infer` from it. If you need a guided migration flow built-in, file a feature request via the `references/feedback.md` skill.
|
|
293
|
+
- **`prisma db push`-style production sync.** `db update` is the quick development path; for production, use migrations (`migration plan` + `db migrate`). PN deliberately does not offer a "push-to-prod-without-a-migration" surface — see `references/migrations.md`.
|
|
294
|
+
- **Studio / GUI database browser.** Use `prisma db schema` for a CLI tree-style summary of the live DB. If you need an interactive UI, file a feature request via the `references/feedback.md` skill.
|
|
295
|
+
|
|
296
|
+
## Reference Files
|
|
297
|
+
|
|
298
|
+
This skill is intentionally body-only; `prisma orm init --help`, `contract infer --help`, and `db sign --help` are the authoritative surfaces for flag-level detail. When in doubt, run `--help` and read the actual command's description rather than guessing from this skill.
|
|
299
|
+
|
|
300
|
+
## Checklist
|
|
301
|
+
|
|
302
|
+
- [ ] Confirmed which path applies (first-touch orientation / greenfield / brownfield) before proposing commands.
|
|
303
|
+
- [ ] **First-touch orientation:** named the contract path back to the user and framed its role (*source of truth from which query types, migrations, and runtime types flow*) before proposing any commands.
|
|
304
|
+
- [ ] **All paths:** brought the project to the *Your first arc* prerequisites (config, contract source, `db.ts`, `DATABASE_URL`, marker row) *before* writing application code.
|
|
305
|
+
- [ ] **All paths:** ran the first arc — one `create` + one `select` against the starter (or inferred) model — and got the round-trip working green.
|
|
306
|
+
- [ ] **All paths:** did *not* edit the contract source as part of the first arc. Schema extension is the *next* move, not the first.
|
|
307
|
+
- [ ] **All paths:** did *not* lead with a feature tour, capability inventory, or recital of CLI commands. Commands surfaced as the user's current move required them.
|
|
308
|
+
- [ ] Confirmed the user's target (`postgres` / `mongodb`) and authoring mode (`psl` / `typescript`).
|
|
309
|
+
- [ ] **First-touch orientation:** read `prisma.config.ts`, the contract source, `db.ts`, and `.env` before proposing anything — didn't assume what the scaffold tool / teammate left in place.
|
|
310
|
+
- [ ] **Greenfield path:** ran `prisma orm init` from the project directory — no positional project-name argument.
|
|
311
|
+
- [ ] **All paths (application projects):** the project ended up in the canonical `src/prisma/contract.{prisma,ts}` + `src/prisma/db.ts` + `migrations/app/` layout (what `init` scaffolds by default). An extension or aggregate-root package keeps its own `src/contract.{prisma,ts}` + `migrations/<timestamp>_<slug>/` layout — do not relocate it.
|
|
312
|
+
- [ ] **Brownfield path:** ran `contract infer --db "$DATABASE_URL" --output src/prisma/contract.prisma`, reviewed the result, then `contract emit` + `db sign` (which by default also sets the `db` ref, so the next `migration plan` chains from the adopted schema; not with `--no-advance-ref` or `--advance-ref <name>`).
|
|
313
|
+
- [ ] Set `DATABASE_URL` in `.env` and confirmed the value is reachable.
|
|
314
|
+
- [ ] Initialised the DB (`db init` greenfield / first-touch orientation) or signed the marker (`db sign` brownfield — by default sets the `db` ref too, with or without `--db`).
|
|
315
|
+
- [ ] Did NOT hand-edit `contract.json` or `contract.d.ts`.
|
|
316
|
+
- [ ] Did NOT set `DATABASE_URL` in `prisma.config.ts`.
|
|
317
|
+
- [ ] Confirmed the user understands what the *next* skill is for their workflow (typically `references/queries.md` for more queries, then `references/contract.md` when they're ready to extend the schema).
|