Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/prisma/prisma-next/llms.txt

Use this file to discover all available pages before exploring further.

The ORM client is the high-level query surface in Prisma Next. It gives you typed model collections with a fluent API for filtering, ordering, pagination, relations, and mutations — all verified against your emitted contract at compile time. You compose queries by chaining methods on a collection; the client builds and executes the required SQL plans for you.

Setting up the ORM client

For a long-lived Node process, the db.orm property is available directly on the postgres() facade. ORM collection accessors use the model name from your contract (capitalized, as declared in your schema):
// db.ts
import postgres from '@prisma-next/postgres/runtime';
import type { Contract } from './.prisma/contract.d';
import contractJson from './.prisma/contract.json' with { type: 'json' };

export const db = postgres<Contract>({ contractJson });

// Access collections by model name (e.g. db.orm.User, db.orm.Post)
const users = await db.orm.User.take(10).all();
For serverless runtimes, use createOrmClient(runtime) inside the request handler instead. See Deploying to serverless runtimes for the full pattern.

Defining collection classes

Collections are classes that extend the base Collection type and are registered on the orm() factory. Each collection maps to a model in your contract and can expose custom query methods:
// orm-client/collections.ts
import { Collection } from '@prisma-next/sql-orm-client';
import type { Contract } from '../.prisma/contract.d';

export class UserCollection extends Collection<Contract, 'User'> {
  byEmail(email: string) {
    return this.where({ email });
  }

  newestFirst() {
    return this.orderBy((u) => u.createdAt.desc());
  }

  emailDomain(domain: string) {
    return this.where((user) => user.email.ilike(`%@${domain}`));
  }
}

export class PostCollection extends Collection<Contract, 'Post'> {
  forUser(userId: string) {
    return this.where({ userId });
  }

  newestFirst() {
    return this.orderBy((post) => post.createdAt.desc());
  }
}
Register collections on the orm() factory. Pass the runtime from db.runtime() (synchronous):
// orm-client/client.ts
import type { Runtime } from '@prisma-next/sql-runtime';
import { orm } from '@prisma-next/sql-orm-client';
import type { ExecutionContext } from '@prisma-next/sql-relational-core/query-lane-context';
import type { Contract } from '../.prisma/contract.d';
import { db } from '../db';
import { UserCollection, PostCollection } from './collections';

const context = db.context as ExecutionContext<Contract>;

export function createOrmClient(runtime: Runtime) {
  return orm({
    runtime,
    context,
    collections: {
      User: UserCollection,
      Post: PostCollection,
    },
  });
}

Filtering with .where()

You can pass a shorthand object filter or a typed expression callback. Both forms can be chained:
// Exact match on one or more fields
const users = await db.orm.User
  .where({ email: 'alice@example.com' })
  .all();

Sorting with .orderBy()

Pass a callback that selects a field and calls a direction method:
const posts = await db.orm.Post
  .orderBy((p) => p.createdAt.desc())
  .all();

Pagination with .take() and .skip()

const page = await db.orm.Post
  .orderBy((p) => p.createdAt.desc())
  .skip(20)
  .take(10)
  .all();

Including relations with .include()

Pass a relation name to eager-load it. You can pass a callback to further constrain the included collection — including nested includes:
const orders = await db.orm.Order
  .where({ userId: currentUserId })
  .where((o) => o.status.in(['shipped', 'delivered']))
  .include('shippingAddress')
  .include('items', (item) =>
    item.include('product', (product) =>
      product
        .include('category')
        .include('images', (img) => img.where({ isPrimary: true }).take(1))
        .include('reviews', (reviews) =>
          reviews
            .where((r) => r.rating.gte(4))
            .orderBy((r) => r.createdAt.desc())
            .take(3)
            .include('author', (a) => a.select('name', 'avatar')),
        ),
    ),
  )
  .all();
Each nested .include() is a fully typed collection: you get the same .where(), .orderBy(), .take(), and .select() methods at every level.

Selecting fields with .select()

Use .select() to return only specific fields. TypeScript infers the return type from the selected columns:
const names = await db.orm.User
  .select('id', 'email', 'displayName')
  .take(50)
  .all();
// names: Array<{ id: string; email: string; displayName: string }>

Fetching results

// Materializes all matching rows into an array
const posts = await db.orm.Post.where({ userId: currentUserId }).all();
.stream() returns an AsyncIterableResult. If execution fails, the iterable throws. Wrap streaming consumers in a try/catch or use a for await that propagates errors naturally.

Creating, updating, and deleting records

const user = await db.orm.User.select('id', 'email', 'kind').create({
  id: crypto.randomUUID(),
  email: 'bob@example.com',
  displayName: 'Bob',
  kind: 'user',
  createdAt: new Date(),
});
update() and delete() require a .where() clause. Calling them without filtering is a compile-time error — Prisma Next enforces this to prevent accidental bulk mutations.

Using withTransaction() for mutations

Wrap related mutations in a transaction to ensure atomicity. The withTransaction helper from @prisma-next/sql-runtime acquires a transaction on the runtime and runs your callback:
import { withTransaction } from '@prisma-next/sql-runtime';

const result = await withTransaction(db.runtime(), async (tx) => {
  const orm = createOrmClient(tx);
  const order = await orm.Order.create({ userId, total });
  await orm.OrderItem.create({ orderId: order.id, productId, quantity });
  return order;
});
All statements inside the callback run on the same connection. If the callback throws, the transaction is rolled back automatically.

Using the ORM client in a serverless context

In per-request runtimes, construct the ORM client inside the request handler using the per-request runtime returned by db.connect():
import { createOrmClient } from './orm-client/client';
import { db } from './prisma/db';

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    await using runtime = await db.connect({ url: env.HYPERDRIVE.connectionString });

    const orm = createOrmClient(runtime);
    const users = await orm.User.newestFirst().take(10).all();

    return Response.json(users);
  },
};
Where createOrmClient is a factory that passes the per-request runtime to orm({...}):
// orm-client/client.ts
import type { Runtime } from '@prisma-next/sql-runtime';
import { orm } from '@prisma-next/sql-orm-client';
import { db } from '../prisma/db';
import { UserCollection, PostCollection } from './collections';

export function createOrmClient(runtime: Runtime) {
  return orm({
    runtime,
    context: db.context,
    collections: {
      User: UserCollection,
      Post: PostCollection,
    },
  });
}

Build docs developers (and LLMs) love