@prisma/orm-sqlite 8.0.0-rc.13-dev.4 → 8.0.0-rc.13-dev.6
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma/orm-sqlite",
|
|
3
|
-
"version": "8.0.0-rc.13-dev.
|
|
3
|
+
"version": "8.0.0-rc.13-dev.6",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -10,17 +10,17 @@
|
|
|
10
10
|
"skills"
|
|
11
11
|
],
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@prisma/orm-family-sql": "8.0.0-rc.13-dev.
|
|
14
|
-
"@prisma/orm-framework": "8.0.0-rc.13-dev.
|
|
15
|
-
"@prisma/orm-target-sqlite": "8.0.0-rc.13-dev.
|
|
16
|
-
"@prisma/orm-toolchain": "8.0.0-rc.13-dev.
|
|
13
|
+
"@prisma/orm-family-sql": "8.0.0-rc.13-dev.6",
|
|
14
|
+
"@prisma/orm-framework": "8.0.0-rc.13-dev.6",
|
|
15
|
+
"@prisma/orm-target-sqlite": "8.0.0-rc.13-dev.6",
|
|
16
|
+
"@prisma/orm-toolchain": "8.0.0-rc.13-dev.6",
|
|
17
17
|
"pathe": "^2.0.3",
|
|
18
18
|
"tinyglobby": "^0.2.17"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@internal/sqlite": "8.0.0-rc.13-dev.
|
|
22
|
-
"@repo/tsconfig": "8.0.0-rc.13-dev.
|
|
23
|
-
"@repo/tsdown": "8.0.0-rc.13-dev.
|
|
21
|
+
"@internal/sqlite": "8.0.0-rc.13-dev.6",
|
|
22
|
+
"@repo/tsconfig": "8.0.0-rc.13-dev.6",
|
|
23
|
+
"@repo/tsdown": "8.0.0-rc.13-dev.6",
|
|
24
24
|
"tsdown": "0.22.14",
|
|
25
25
|
"typescript": "5.9.3"
|
|
26
26
|
},
|
package/skills/prisma-8/SKILL.md
CHANGED
|
@@ -70,7 +70,7 @@ You do **not** need a `collect()` / `toArray()` helper — `await` is enough. In
|
|
|
70
70
|
const rows: Promise<User[]> = db.orm.public.User.select('id', 'email').all().toArray();
|
|
71
71
|
|
|
72
72
|
// Iterate — decode and handle rows one at a time. Whether the raw rows are
|
|
73
|
-
// also fetched incrementally depends on the
|
|
73
|
+
// also fetched incrementally depends on the `cursor` option; see *Streaming* below.
|
|
74
74
|
for await (const user of db.orm.public.User.select('id', 'email').all()) {
|
|
75
75
|
process(user);
|
|
76
76
|
}
|
|
@@ -127,12 +127,12 @@ await db.close();
|
|
|
127
127
|
|
|
128
128
|
## Streaming
|
|
129
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
|
|
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 `cursor` option, which `postgres()` and `postgresServerless()` (`@prisma/orm-postgres/serverless`) both accept:
|
|
131
131
|
|
|
132
|
-
- **
|
|
133
|
-
-
|
|
132
|
+
- **Default (cursors off), on a client and on a connection:** 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
|
+
- **`cursor` set in the options of `postgres()` or `postgresServerless()` (`{ batchSize: 100 }`; `{}` or `{ batchSize: undefined }` for batches of 100; a `batchSize` that is not a positive integer fails the factory call; there is no flag that turns cursors off):** the driver reads through a server-side cursor in batches of that size, so `for await` over `db.orm...all()` or `db.runtime().query(plan)` really does stream, and an early `break` stops reading. In a Worker, put the option only on a second serverless client that the streaming paths open their connections from, and keep the serverless client every other path uses without it. On a connection with cursors on, finish or `break` the `for await` loop before sending another query through `db`: the cursor holds the connection's only database connection until the loop ends, so a query inside the loop waits forever. On a client, the same query takes another database connection from the pool. Behind Cloudflare Hyperdrive, reads with cursors on hang, so the streaming paths hang there and the other paths do not.
|
|
134
134
|
|
|
135
|
-
There is no `.stream()` method on
|
|
135
|
+
There is no `.stream()` method on a client or on a connection.
|
|
136
136
|
|
|
137
137
|
## Prepared statements (Postgres, SQLite)
|
|
138
138
|
|
|
@@ -216,11 +216,10 @@ Target-specific pitfalls live in the per-target guides.
|
|
|
216
216
|
- **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.
|
|
217
217
|
- **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`.
|
|
218
218
|
- **`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`.
|
|
219
|
-
- **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`.
|
|
220
219
|
- **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`.
|
|
221
220
|
- **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.
|
|
222
221
|
- **Mongo ORM aggregates.** No `.aggregate(...)` / `.groupBy(...)` on `db.orm.<root>`. Workaround: express aggregations through `db.query.from(...).group(...).build()` and `runtime.query(plan)`.
|
|
223
|
-
- **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.
|
|
222
|
+
- **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. The façade does not re-export them yet.
|
|
224
223
|
- **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`).
|
|
225
224
|
|
|
226
225
|
## Reference Files
|
|
@@ -13,7 +13,8 @@ This skill covers the **runtime entry point** — `db.ts` — and how to compose
|
|
|
13
13
|
- User wants to switch between the Postgres, SQLite, and Mongo façades.
|
|
14
14
|
- User wants to wrap operations in `db.transaction(...)` (Postgres and SQLite).
|
|
15
15
|
- User is running a one-off script (`tsx my-script.ts`, Node CLI, CI task) and the process won't exit after queries finish, or they need script teardown (`db.close()`, `await using`).
|
|
16
|
-
- User
|
|
16
|
+
- User is deploying to a per-request runtime (Cloudflare Workers + Hyperdrive, AWS Lambda, Vercel Edge, Deno Deploy, Bun edge) and needs `postgresServerless()` with `postgres.connect({ url })` per request.
|
|
17
|
+
- User mentions: *db.ts, postgres(), mongo(), middleware, lints, budgets, cache, query log, slow query, DATABASE_URL, .env, connection pool, poolOptions, dev vs prod, transactions, read replicas, multi-database, script won't exit, hangs, db.close, db.end, close connection, pool.end, await using, serverless, edge, Cloudflare Workers, Hyperdrive, postgresServerless, connect per request*.
|
|
17
18
|
|
|
18
19
|
## When Not to Use
|
|
19
20
|
|
|
@@ -26,9 +27,9 @@ This skill covers the **runtime entry point** — `db.ts` — and how to compose
|
|
|
26
27
|
|
|
27
28
|
## Key Concepts
|
|
28
29
|
|
|
29
|
-
- **`db.ts` is the runtime entry point.** Imports the runtime factory from the `@internal/<target>` façade (`@internal/postgres/runtime`, `@internal/sqlite/runtime`, or `@internal/mongo/runtime`), the contract artefacts (`contract.json` + the `Contract` type from `contract.d.ts`), and any middleware. Exports a `db` value the rest of your app imports.
|
|
30
|
+
- **`db.ts` is the runtime entry point.** Imports the runtime factory from the `@internal/<target>` façade (`@internal/postgres/runtime`, `@internal/sqlite/runtime`, or `@internal/mongo/runtime`), the contract artefacts (`contract.json` + the `Contract` type from `contract.d.ts`), and any middleware. Exports a `db` value the rest of your app imports. On a per-request runtime, `db.ts` imports `postgresServerless` from `@internal/postgres/serverless` instead and exports the serverless client `postgres`; see *Workflow — Serverless and per-request runtimes*.
|
|
30
31
|
- **The façade's runtime factory is the only surface user-authored `db.ts` imports from.** Each factory is a *default* export. For Postgres: `import postgres from '@internal/postgres/runtime'`; SQLite: `import sqlite from '@internal/sqlite/runtime'`; Mongo: `import mongo from '@internal/mongo/runtime'`. The factory signature is `<Target><Contract>(options)` — a single type parameter (the `Contract` type from `contract.d.ts`), and one options object.
|
|
31
|
-
- **Lazy connect.** The factory does not connect to the database synchronously.
|
|
32
|
+
- **Lazy connect.** The factory does not connect to the database synchronously. `db.sql` and `db.orm` are available immediately; the driver / pool is instantiated on the first call that needs a runtime (or when you explicitly call `await db.connect({ url })`, which binds the client to that URL), and the pool opens its first database connection on the first query. This is why `db.ts` can be imported in modules that load before the env is ready.
|
|
32
33
|
- **Middleware composes in order.** The first middleware in the `middleware: [...]` array runs *outermost* — its `beforeQuery` sees the operation first, and `interceptQuery` hooks are consulted in registration order with the first non-`undefined` result winning. Register a cache first so it gets first claim on a hit. Every middleware's `afterQuery` still fires on a cache hit, with `result.source: 'middleware'`.
|
|
33
34
|
- **`prisma.config.ts` vs `.env`.** The config (`definePrismaConfig({ orm: ormConfig({ contract, db, extensions, migrations }) })` — see `references/contract.md`) is for static project shape: contract path, installed extensions, migrations directory, default connection string. `.env` is for per-environment values (`DATABASE_URL`, secrets). 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 `DATABASE_URL` in the config file leaks credentials and bypasses per-env overrides.
|
|
34
35
|
- **Build-system / dev-server integration is a separate skill.** `vite dev` auto-emit lives in `references/build.md`. The runtime side (this skill) reads `contract.json` / `contract.d.ts` regardless of how they got onto disk, so the two skills compose cleanly.
|
|
@@ -63,7 +64,7 @@ The Mongo façade has the same construction shape — `import mongo from '@inter
|
|
|
63
64
|
|
|
64
65
|
## Workflow — Running as a script (teardown)
|
|
65
66
|
|
|
66
|
-
The concept: short scripts that connect, query, then expect the process to exit will **hang on Postgres** because the
|
|
67
|
+
The concept: short scripts that connect, query, then expect the process to exit will **hang on Postgres** because the `pg.Pool` owned by `postgres()` keeps Node's event loop alive. The data round-trip succeeds; the script never exits. Call `await db.close()` before the script returns (or use `await using` **at the top of a script module** so teardown runs when the module exits — see the block-scope warning below for why this matters).
|
|
67
68
|
|
|
68
69
|
**Plain shape** — export `db` from `db.ts`, import it in the script, close at the end:
|
|
69
70
|
|
|
@@ -93,7 +94,9 @@ console.log(user);
|
|
|
93
94
|
// db.close() runs automatically when the script module exits.
|
|
94
95
|
```
|
|
95
96
|
|
|
96
|
-
### `await using` is **block-scoped** — do not put it inside a request handler
|
|
97
|
+
### `await using` on a `postgres()` client is **block-scoped** — do not put it inside a request handler
|
|
98
|
+
|
|
99
|
+
This rule is about the long-lived `postgres()` client. On a per-request runtime, `await using db = await postgres.connect({ url })` inside the handler is the correct pattern; see *Workflow — Serverless and per-request runtimes* below.
|
|
97
100
|
|
|
98
101
|
This is the most important rule in this section. `await using db = postgres(...)` disposes when the *enclosing block* exits. In a script module, that block is the module body and disposal fires at process exit — fine. In a request handler, the enclosing block is the handler function, so disposal fires **after every request** — a fresh `pg.Pool` per call, TCP-connect storm, hot loop tearing connections up and down.
|
|
99
102
|
|
|
@@ -126,12 +129,47 @@ Servers (HTTP handlers, workers in a request loop) **do not call `db.close()`**
|
|
|
126
129
|
**Semantics:**
|
|
127
130
|
|
|
128
131
|
- **`close()` is idempotent.** Calling it twice is a no-op.
|
|
129
|
-
- **`close()` is terminal.** There is no reconnect on a closed `db` — construct a new client if you need
|
|
132
|
+
- **`close()` is terminal.** There is no reconnect on a closed `db` — construct a new client if you need to reach the database again. After close, `db.runtime()`, `db.connect(...)`, `db.transaction(...)`, and `db.prepare(...)` reject with `Error('<target> client is closed')` (e.g. `'Postgres client is closed'`, `'SQLite client is closed'`, `'Mongo client is closed'`).
|
|
130
133
|
- **`close()` does not abort in-flight queries.** `await` outstanding work before calling `close()`. Async iterators from `db.runtime().query(plan)` and `PreparedStatement` handles held after `close()` fail on their next call.
|
|
131
134
|
- **Ownership.** `close()` releases only what the façade constructed (`pg.Pool` from `{ url }`, `MongoClient` from `{ url }` / `{ uri, dbName }`, SQLite handle from `{ path }`). If you supplied your own `pg.Pool` / `pg.Client` (Postgres `pg:` option), `mongodb.MongoClient` (Mongo `mongoClient:` option), or a pre-built `binding`, `db.close()` does **not** touch those — you own their lifecycle.
|
|
132
135
|
|
|
133
136
|
**`db.end()` does not exist.** The universal `node-postgres` name is `pool.end()` on a `pg.Pool`; the Prisma 8 runtime client is not a `pg.Pool`. The right call is `await db.close()`.
|
|
134
137
|
|
|
138
|
+
## Workflow — Serverless and per-request runtimes
|
|
139
|
+
|
|
140
|
+
The concept: a per-request runtime (Cloudflare Workers + Hyperdrive, AWS Lambda, Vercel Edge, Deno Deploy, Bun edge) must not keep a database connection at module scope. Use `postgresServerless()` from `@internal/postgres/serverless`. It returns a **serverless client**; name it `postgres`. It holds no database connection. In each request, open a **connection** named `db` with `await using db = await postgres.connect({ url })`. A connection owns one database connection, a `pg.Client`, and `await using` closes it when the handler returns. `db` has the members of a `postgres()` client except `connect`.
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
// src/prisma/db.ts — the serverless client: module scope, built once per isolate, holds no database connection
|
|
144
|
+
import postgresServerless from '@internal/postgres/serverless';
|
|
145
|
+
import type { Contract } from './contract.d';
|
|
146
|
+
import contractJson from './contract.json' with { type: 'json' };
|
|
147
|
+
|
|
148
|
+
export const postgres = postgresServerless<Contract>({ contractJson });
|
|
149
|
+
|
|
150
|
+
// src/worker.ts
|
|
151
|
+
import { postgres } from './prisma/db';
|
|
152
|
+
|
|
153
|
+
export default {
|
|
154
|
+
async fetch(request: Request, env: Env): Promise<Response> {
|
|
155
|
+
await using db = await postgres.connect({ url: env.HYPERDRIVE.connectionString });
|
|
156
|
+
const users = await db.orm.public.User.all();
|
|
157
|
+
return Response.json(users);
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The rule to remember: inside a request, `db` does everything the `db` from `postgres()` does, so any documented `db.orm...`, `db.sql...`, `db.raw...`, `db.transaction(...)`, `db.prepare(...)` or `db.runtime().query(...)` snippet works unchanged. Await every query before the `await using` scope ends: the connection closes when the scope ends, so a query returned from the scope without `await` (`return db.orm...` instead of `return await db.orm...`) fails when its rows are read, with `CONTRACT.MARKER_READ_FAILED` (whose cause is `DRIVER.NOT_CONNECTED`) under the default options, or `DRIVER.NOT_CONNECTED` after an earlier awaited query or with `verifyMarker: false`.
|
|
163
|
+
|
|
164
|
+
- **`connect` connects before it returns.** When the database refuses the connection, rejects the credentials, or does not answer within 20 seconds, `postgres.connect({ url })` rejects with `DRIVER.CONNECTION_FAILED` and leaves nothing open; an empty URL, a string that is not a URL, or a scheme other than `postgres://` or `postgresql://` rejects with `RUNTIME.BINDING_INVALID`. Handle an unreachable database at the `connect` call, not at the first query. Answer a request that needs no query, such as an unknown route, before `connect`, because `connect` opens a database connection whether or not a query follows.
|
|
165
|
+
- **Never call `connect` at module scope.** A connection opened there is shared by every request in the isolate, and fails in four ways: its database connection goes stale after the isolate idles, concurrent requests queue behind each other on one `pg.Client`, nothing closes it when a request ends, and Cloudflare Workers reject a socket used across requests.
|
|
166
|
+
- **`db.orm` is the default way to use the ORM.** Build `orm({ runtime: db.runtime(), context: db.context, collections })` only for custom collection classes, and build it inside the request.
|
|
167
|
+
- **Anything that takes a runtime gets `db.runtime()`.** That includes `orm({ runtime, ... })`, `withTransaction(runtime, fn)` and a prepared statement's `query(runtime, params)`. Do not pass `db` itself to `orm()`.
|
|
168
|
+
- **Inside `db.transaction(async (tx) => ...)`, run every query through `tx`.** A connection has one database connection, so inside `db.transaction(async (tx) => ...)` a query through `db` is not independent of the transaction; run every query through `tx`. A query that uses the connection's database connection directly, such as a `db.orm` read, a `db.orm` create of one row, an `updateAll(...)` or `deleteAll()`, or `db.runtime().query(...)`, runs inside the open transaction without saying so. An operation that asks for a database connection of its own, such as a `db.orm` `update(...)` or `delete()` of one row, `db.runtime().connection()`, a `db.orm` create that also writes related rows, or a nested `db.transaction(...)`, waits for the database connection the transaction holds, and the request hangs.
|
|
169
|
+
- **After `db.close()`** (or the end of the `await using` scope), `db.runtime()`, ORM queries, `db.transaction(...)` and `db.prepare(...)` fail with `DRIVER.NOT_CONNECTED`. Call `postgres.connect({ url })` again for a new connection.
|
|
170
|
+
|
|
171
|
+
How connections read rows, and the `cursor` option on the serverless client (with the Hyperdrive caveat), is in `references/queries.md` § *Streaming*.
|
|
172
|
+
|
|
135
173
|
## Workflow — Custom middleware (query log, slow-query warning)
|
|
136
174
|
|
|
137
175
|
The concept: a middleware is a plain object with `name`, `familyId: 'sql'`, and any of the hooks `beforeQuery`, `interceptQuery`, `afterQuery`. There is no separate telemetry package — observe queries with `afterQuery`, which fires once per execution after the rows are consumed, with `result.latencyMs`, `result.rowCount`, and `result.source` (`'driver'` or `'middleware'` for a cache hit). The `SqlMiddleware` type comes from `@prisma/orm-postgres/family-runtime`. `examples/prisma-8-demo/src/prisma/slow-query-warning.ts` is the canonical example:
|
|
@@ -231,7 +269,7 @@ Order matters: `beforeQuery` runs in registration order for every middleware bef
|
|
|
231
269
|
|
|
232
270
|
## Workflow — Configure the connection
|
|
233
271
|
|
|
234
|
-
The concept: the runtime takes one of three binding shapes — `url`, `pg` (a pre-constructed `pg.Pool` or `pg.Client`), or `binding` (an explicit kind tag). They're mutually exclusive. The `pg` form is for projects that already manage their own pool (e.g. a Lambda layer); `url` is the default. Pool tuning is `poolOptions.connectionTimeoutMillis` / `poolOptions.idleTimeoutMillis` — *not* `driverOptions`.
|
|
272
|
+
The concept: the runtime takes one of three binding shapes — `url`, `pg` (a pre-constructed `pg.Pool` or `pg.Client`), or `binding` (an explicit kind tag). They're mutually exclusive. The `pg` form is for projects that already manage their own pool (e.g. a Lambda layer); `url` is the default. Pool tuning is `poolOptions.connectionTimeoutMillis` / `poolOptions.idleTimeoutMillis` — *not* `driverOptions`. Reads are buffered by default; setting `cursor` (`{ batchSize: 100 }`; `{}` or `{ batchSize: undefined }` for batches of 100; a `batchSize` that is not a positive integer fails the factory call) makes them stream through a server-side cursor, and there is no flag that turns cursors off (see `references/queries.md` § *Streaming*). `postgresServerless()` takes the same `cursor` option.
|
|
235
273
|
|
|
236
274
|
```typescript
|
|
237
275
|
// Default — URL string, factory constructs the pool.
|
|
@@ -242,6 +280,7 @@ postgres<Contract>({
|
|
|
242
280
|
connectionTimeoutMillis: 20_000,
|
|
243
281
|
idleTimeoutMillis: 30_000,
|
|
244
282
|
},
|
|
283
|
+
// cursor: { batchSize: 100 }, // optional — stream reads in batches instead of buffering
|
|
245
284
|
});
|
|
246
285
|
|
|
247
286
|
// BYO pool — pass a pg.Pool you already created.
|
|
@@ -337,7 +376,7 @@ The runtime side (this skill) is the same regardless: `db.ts` reads `contract.js
|
|
|
337
376
|
5. **Importing middleware from a non-existent package or subpath.** There is no `@internal/postgres/middleware` subpath and no `@internal/middleware-telemetry` package. `lints` / `budgets` / `SqlMiddleware` come from `@prisma/orm-postgres/family-runtime`; the cache comes from `@prisma/orm-extension-middleware-cache`; a query log or telemetry hook is a custom `afterQuery` middleware (above).
|
|
338
377
|
6. **Confabulating lint / budget option names.** Lints take `severities` (with the five keys above), not `requireWhere` / `maxRowsWithoutLimit`. Budgets use `maxLatencyMs` (not `maxDurationMs`) plus `maxRows` / `defaultTableRows` / `tableRows`. When in doubt, read the source.
|
|
339
378
|
7. **Switching targets without re-emitting.** The contract artefacts are target-shaped; emit after the target change.
|
|
340
|
-
8. **Script hangs after queries finish on Postgres.** The `pg.Pool` keeps Node's event loop alive. Solution: `await db.close()` before the script returns, or `await using db = postgres<Contract>(...)` at the top of a script module. Do not put `await using db = postgres(...)` inside a request handler — it's block-scoped and would close the pool after every request. The right server pattern is a module-level singleton in `db.ts` that lives for the process lifetime.
|
|
379
|
+
8. **Script hangs after queries finish on Postgres.** The `pg.Pool` keeps Node's event loop alive. Solution: `await db.close()` before the script returns, or `await using db = postgres<Contract>(...)` at the top of a script module. Do not put `await using db = postgres(...)` inside a request handler — it's block-scoped and would close the pool after every request. The right server pattern is a module-level singleton in `db.ts` that lives for the process lifetime. On a per-request runtime, use `postgresServerless()` and `await using db = await postgres.connect({ url })` in the handler instead (see *Workflow — Serverless and per-request runtimes*).
|
|
341
380
|
|
|
342
381
|
## What Prisma 8 doesn't do yet
|
|
343
382
|
|