Loading...
Loading...
Write Prisma Next queries — pick a lane (`db.orm.<Model>` for CRUD and includes, `db.sql.<table>` SQL builder for set-builder shapes the ORM doesn't express), filter / project / sort / paginate, eager-load relations with `.include(...)`, transactions via `db.transaction(...)`, aggregates via `.aggregate(...)`. Use for query, where, select, orderBy, take, skip, include, eager load, first, all, count, aggregate, create, update, delete, upsert, returning, transaction, db.transaction, drizzle-style, kysely-style, prisma client, db.close, script, script won't exit, hangs, close connection, db.end, pool.end, await using. Also covers result consumption (`.all()` is a Thenable — just `await` it; no `collect()` / `toArray()` helper needed), single-consumption semantics (`RUNTIME.ITERATOR_CONSUMED`), aggregate nullability (`count` returns `number`, `sum/avg/min/max` return `number | null` per SQL semantics), and range conditions (chain `.where()` clauses or use `and(...)` — there is no `.between(...)`).
npx skill4agent add prisma/prisma-next prisma-next-queriesEdit your data contract. Prisma handles the rest.
db.transaction(...)countsumavgprisma-next-contractdb.tsprisma-next-runtimeprisma-next-debugdbsrc/prisma/db.ts@prisma-next/<target>/runtimedb.orm.<Model>db.orm.User.where(...).select(...).orderBy(...).all()Contractdb.sql.<table>db.sql.userJOINdb.sql| Need | Choose | Why |
|---|---|---|
| Standard CRUD with relations | ORM ( | Highest ergonomics; fully typed; model-shaped. |
| Eager-load related records | ORM | Composes with |
| Aggregate (count, sum, avg) | ORM | Typed result; works with grouping ( |
| ORM mutations (returns updated rows) or | ORM returns inserted/updated rows; SQL builder exposes |
Computed projection (e.g. | SQL builder ( | The ORM projects model fields; arbitrary expression projection is the SQL builder's seam. |
Complex | SQL builder | The ORM doesn't express arbitrary joins. |
Postgres-specific feature ( | SQL builder, falling back to extension operators when the extension provides them | DSL first; extensions can contribute operators ( |
db.orm.<Model>.all().first().count().aggregate(...)u.field.<op>(value)// src/queries/users.ts — one directory deep under src/, so the import is '../prisma/db'
import { db } from '../prisma/db';
// Find one record by primary key shorthand.
const user = await db.orm.User.first({ id: userId });
// Returns the full row or `null`.
// Find one matching a predicate.
const alice = await db.orm.User
.where((u) => u.email.eq('alice@example.com'))
.first();
// Find many with projection, sort, and limit.
const recentUsers = await db.orm.User
.select('id', 'email', 'createdAt')
.orderBy((u) => u.createdAt.desc())
.take(10)
.all();.where(...)// Lambda form — full expression power.
db.orm.User.where((u) => u.email.eq('alice@example.com'));
// Shorthand object form — equality on the named fields.
db.orm.User.where({ kind: 'admin' });.eq.neq.lt.lte.gt.gte.like.ilike.in([...]).isNull().isNotNull()pgvector.cosineDistance(...)postgis.within(...).intersectsBbox(...).distanceSphere(...)cipherstash.cipherstashEq(...).cipherstashGt(...).between(a, b).where(...)and(...)// Chained .where() — each clause AND-composes with the previous one.
await db.orm.Sale
.where((s) => s.day.gte(start))
.where((s) => s.day.lte(end))
.all();
// Equivalent with an explicit `and(...)` inside one clause.
import { and } from '@prisma-next/sql-orm-client'; // façade re-export pending — see *What PN doesn't do yet*
await db.orm.Sale
.where((s) => and(s.day.gte(start), s.day.lte(end)))
.all();.where()and(...)betweenandornot.some(...).none(...).every(...)@prisma-next/sql-orm-clientimport { and, or, not } from '@prisma-next/sql-orm-client';
await db.orm.User
.where((u) =>
and(
or(u.kind.eq('admin'), u.email.ilike('%@example.com')),
not(u.posts.none((p) => p.title.ilike('%draft%'))),
),
)
.all();.orderBy(...).asc().desc().take(n).skip(n)await db.orm.Post
.where((p) => p.authorId.eq(userId))
.orderBy([(p) => p.createdAt.desc(), (p) => p.id.desc()])
.take(20)
.all();
// Cursor pagination — order by an indexed unique column and filter past the cursor.
const cursor = lastPostFromPreviousPage.createdAt;
await db.orm.Post
.where((p) => p.createdAt.lt(cursor))
.orderBy((p) => p.createdAt.desc())
.take(20)
.all();.first().first({ pk }).all().first()LIMIT 1.first({ pk }).all()LIMITawait.toArray()for await.all()AsyncIterableResult<Row>PromiseLike<Row[]>AsyncIterable<Row>const users = await db.orm.User.select('id', 'email').all();
// ^? Row[] ← the Thenable resolves to a real array. This is the default idiom.collect()toArray()awaitawaitthen(...)// Explicit buffering — same outcome as `await ... .all()`, useful when you
// want a named Promise<Row[]> to thread through downstream code.
const rows: Promise<User[]> = db.orm.User.select('id', 'email').all().toArray();
// Streaming — process rows one at a time without buffering the whole result.
// Use for genuinely large result sets (anything that wouldn't fit comfortably
// in memory) or pipelines where you can start work before all rows arrive.
for await (const user of db.orm.User.select('id', 'email').all()) {
process(user);
}.first()LIMIT 1const user = await db.orm.User.where({ id }).all().first();
// ^? Row | null ← buffers, returns the first row or null. Issues no LIMIT.
const required = await db.orm.User.where({ id }).all().firstOrThrow();
// ^? Row ← buffers; throws `RUNTIME.NO_ROWS` if empty..first()LIMIT 1.all().first()AsyncIterableResultawait.toArray()for awaitRUNTIME.ITERATOR_CONSUMED// Bad — second await throws RUNTIME.ITERATOR_CONSUMED.
const result = db.orm.User.select('id', 'email').all();
const a = await result;
const b = await result;
// Good — buffer once, reuse the array.
const users = await db.orm.User.select('id', 'email').all();
const a = users;
const b = users;collect(...)toArray(...).all()await.include.include('<relation>', (branch) => branch.<chain>).where.select.orderBy.takeawait db.orm.User
.select('id', 'email')
.include('posts', (post) =>
post
.select('id', 'title', 'createdAt')
.orderBy((p) => p.createdAt.desc())
.take(5),
)
.take(10)
.all();
// → Array<{ id, email, posts: Array<{ id, title, createdAt }> }>1:N → 1:NUser → posts → commentslateraljsonAggprisma-next-contractprisma-next-queries// Create — returns the inserted row.
const user = await db.orm.User.create({ id, email, displayName, kind, createdAt });
// Create with selected return — narrows the return shape.
const summary = await db.orm.User
.select('id', 'email', 'kind')
.create({ id, email, displayName, kind, createdAt });
// Update by predicate.
await db.orm.User.where({ id }).update({ email: newEmail });
// Update with selected return.
await db.orm.User
.where({ id })
.select('id', 'email', 'kind')
.update({ email: newEmail });
// Delete by predicate.
await db.orm.User.where({ id }).delete();
// Upsert — typed by the create branch's shape.
await db.orm.User
.select('id', 'email', 'kind', 'createdAt')
.upsert({
create: { id, email, displayName, kind, createdAt: new Date() },
update: { email, displayName, kind },
});.returning(...)const totals = await db.orm.User.aggregate((aggregate) => ({
totalUsers: aggregate.count(),
}));
const adminTotals = await db.orm.User
.where({ kind: 'admin' })
.aggregate((aggregate) => ({
adminUsers: aggregate.count(),
}));
// Group-by + aggregate.
const byKind = await db.orm.User
.groupBy('kind')
.having((having) => having.count().gte(minUsers))
.aggregate((aggregate) => ({
totalUsers: aggregate.count(),
}));aggregate.count().sum(field).avg(field).min(field).max(field)| Aggregate | Type | Empty result |
|---|---|---|
| | |
| | |
| | |
| | |
| | |
const revenue = await db.orm.Sale
.where((s) => s.day.gte(start))
.aggregate((a) => ({ total: a.sum('amount') }));
// revenue.total: number | null
const safe = revenue.total ?? 0; // ← apply at the consumption site, not in the aggregate spec.?? 0sumdb.sql.<table>db.sql.<table>db.runtime().execute(plan)JOIN// src/queries/posts.ts — adjust the relative import to match file depth.
import { db } from '../prisma/db';
// Select with predicate and limit.
const plan = db.sql.post
.select('id', 'title', 'userId', 'createdAt')
.where((f, fns) => fns.eq(f.userId, userId))
.limit(limit)
.build();
const rows = await db.runtime().execute(plan);.where(...)(fields, fns)fieldsfnsfns.eqfns.nefns.gtfnsfns.distanceSpherefns.cosineDistanceINSERTUPDATEDELETERETURNING// Insert and return selected columns.
const plan = db.sql.user
.insert({ email })
.returning('id', 'email')
.build();
const [row] = await db.runtime().execute(plan);
// Update with predicate and returning.
const updatePlan = db.sql.user
.update({ email: newEmail })
.where((f, fns) => fns.eq(f.id, userId))
.returning('id', 'email')
.build();
const rows = await db.runtime().execute(updatePlan);
// Delete with predicate.
const deletePlan = db.sql.user
.delete()
.where((f, fns) => fns.eq(f.id, userId))
.build();
await db.runtime().execute(deletePlan);.returning(...)returning// Project a computed expression alongside model fields.
const plan = db.sql.cafe
.select('id', 'name')
.select('meters', (f, fns) => fns.distanceSphere(f.location, point))
.orderBy((f, fns) => fns.distanceSphere(f.location, point), { direction: 'asc' })
.orderBy((f) => f.id, { direction: 'asc' })
.limit(limit)
.build();
const rows = await db.runtime().execute(plan);
// Self-join with an alias.
db.sql.post
.innerJoin(db.sql.post.as('p2'), (f, fns) => fns.ne(f.p1.userId, f.p2.userId))
// ...
.build();db.transaction(fn)txtx.ormtx.sqldb.ormdb.sqltx.execute(plan)await db.transaction(async (tx) => {
const user = await tx.orm.User.create({ id, email });
await tx.orm.Post.create({ userId: user.id, title: 'hello' });
// SQL-builder plan inside the transaction.
const plan = tx.sql.post.update({ status: 'archived' })
.where((f, fns) => fns.lt(f.createdAt, cutoff))
.build();
await tx.execute(plan);
// If anything throws, all three operations roll back.
});db.transaction(...)tsx my-script.tsawait db.close()prisma-next-runtimeawait using// src/scripts/seed.ts
import { db } from '../prisma/db';
for (const u of users) {
await db.orm.User.create(u);
}
console.log('Seeded.');
await db.close();db.sqldb.orm.all().all()LIMIT.first()LIMIT 1.first({ pk })collect()toArray().all().all()AsyncIterableResult<Row>PromiseLike<Row[]>await collection.all()Row[]AsyncIterableResultRUNTIME.ITERATOR_CONSUMEDcount()?? 0count()numbernumber | null0?? 0sumavgminmaxnumber | null.between(a, b).where((m) => m.field.gte(a)).where((m) => m.field.lte(b))and(m.field.gte(a), m.field.lte(b)).where()andornot@prisma-next/sql-orm-clientdb.sql.from(tables.user)db.sql.<tableName>.select(...)db.schema.tablesdb.execute(plan)db.runtime().execute(plan)tx.execute(plan)capabilities: { includeMany: true }prisma-next.config.tsdefineConfigcapabilitieslateraljsonAggreturningextensions: [...]prisma-next-contractdb.sql.raw(...).stream()db.execute(plan).create.update.delete.first.all.aggregatedb.runtime().execute(...)groupBy(...).aggregate(...).sort().slice().orderBy(...).take(...)db.sql.<table>GROUP BYORDER BYLIMITandornot@prisma-next/sql-orm-clientTML-2526@prisma-next/sql-orm-client@prisma-next/postgres/runtimeprisma-next-feedback.orderBy(...).take(...)db.orm.<Model>.groupBy(...).aggregate(...)Promise<Array<Group & Aggregates>>db.sql.<table>GROUP BYORDER BYLIMITprisma-next-feedbackdb.sql.raw(...)prisma-next-feedback.sqldb.runtime().execute(plan).sqlprisma-next-feedbackEXPLAIN.explain()pg.Poolpg:prisma-next-runtimeEXPLAIN ANALYZEprisma-next-feedback.stream().skip(n).take(m)pg.Clientpg:prisma-next-feedbackdb.$transaction([call1, call2])db.transaction(async (tx) => { ... })prisma-next-feedback.include(...).include(...)lintsprisma-next-runtimeWHEREDELETEUPDATELIMITSELECTexamples/prisma-next-demo/src/orm-client/examples/prisma-next-demo/src/queries/packages/3-extensions/sql-orm-client/src/packages/2-sql/4-lanes/sql-builder/src/.first().first({ pk }).all().all()awaitcollect()toArray()for awaitsumavgminmax?? 0count()number.where(...)and(...).between(...)andornot@prisma-next/sql-orm-clientdb.runtime().execute(plan)tx.execute(plan)db.transaction(async (tx) => { ... })db.sql.raw.stream()db.batch.between(...)capabilitiesdefineConfigdb.sql.from(tables.user)prisma-next-feedbackdb.sql.<table>groupBy(...).aggregate(...)