# Reading data (/docs/orm/fundamentals/reading-data)

> For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL.

Fetch one record or many with Prisma ORM, then filter, select, sort, paginate, and iterate the results.

Location: ORM > Fundamentals > Reading data

This page shows how to read data with Prisma ORM: fetching [many records or one](#fetch-many-records-or-one), [filtering](#filter-records), [selecting fields](#select-fields), [sorting and paginating](#sort-and-paginate), [counting](#count-records), and [iterating large results](#iterate-a-large-result).

Every query chains methods on a model. The last call in the chain says what you want back and runs the query. Usually that is `.all()` or `.first()`:

  

#### PostgreSQL

```typescript
import { db } from "./prisma/db";

// Every published post
const posts = await db.orm.public.Post.where({ published: true }).all();

// One user, or null
const user = await db.orm.public.User.where({ email: "alice@prisma.io" }).first();
```

#### MongoDB

```typescript
import { db } from "./prisma/db";

// Every published post
const posts = await db.orm.posts.where({ published: true }).all();

// One user, or null
const user = await db.orm.users.where({ email: "alice@prisma.io" }).first();
```

`npm create prisma@latest` scaffolds a new project. `npx prisma orm init` adds Prisma ORM to a project you already have.

Your contract is the `contract.prisma` file that replaced `schema.prisma`. It is where you declare your models, and every result on this page is typed from it. See [Core concepts](https://www.prisma.io/docs/orm/core-concepts) for how the contract and the client fit together.

`db` is the client you create once in `src/prisma/db.ts`. `npx prisma orm init` writes that file for you. Commit it; unlike Prisma ORM 7's generated client, it is your code. `db.ts` reads two generated files that `npx prisma contract emit` writes next to it; you do not edit those. These examples are written from a file in `src/`, so the import is `./prisma/db`; from elsewhere, change the relative path.

`db.orm` holds your models. On PostgreSQL you reach a model through the database schema it is in: in `db.orm.public.User`, `User` is the model name and `public` is the PostgreSQL schema. It is `public` unless the model sits in a `namespace` block in your contract; see [Coming from Prisma ORM 7](https://www.prisma.io/docs/orm/coming-from-prisma-orm-7#schema). On MongoDB there is no schema segment, and the name is the collection: `db.orm.users`.

You can write `.where`, `.select`, `.orderBy`, `.limit`, and `.offset` in any order. `.cursor(...)` must come after `.orderBy(...)`.

For Prisma ORM 7 users, `findMany` and `findFirst` / `findUnique` map directly onto `.all()` and `.first()` ([Coming from Prisma ORM 7](https://www.prisma.io/docs/orm/coming-from-prisma-orm-7) maps the rest of the API):

```diff
- const posts = await prisma.post.findMany({ where: { published: true } });
+ const posts = await db.orm.public.Post.where({ published: true }).all();

- const user = await prisma.user.findUnique({ where: { email } });
+ const user = await db.orm.public.User.where({ email }).first();

- const page = await prisma.post.findMany({ take: 20, skip: 20 });
+ const page = await db.orm.public.Post.limit(20).offset(20).all();
```

`.first()` asks the database for one row. `findUnique` and `findFirst` both become `.first()`, so if several records match you get one of them, in no guaranteed order; add `.orderBy(...)` to choose.

## Example schema [#example-schema]

All examples on this page are based on the contract below. The file is `contract.prisma`, and it is still written in the Prisma schema language, so it looks like the `schema.prisma` you already know. `@default(cuid(2))` generates a CUID version 2 id.

**Expand for sample schema**

#### PostgreSQL

```prisma
model User {
  id        String   @id @default(cuid(2))
  email     String   @unique
  name      String?
  createdAt DateTime @default(now())
  posts     Post[]
}

model Post {
  id        String   @id @default(cuid(2))
  title     String
  content   String?
  published Boolean
  authorId  String
  author    User     @relation(fields: [authorId], references: [id])
  createdAt DateTime @default(now())
}
```

#### MongoDB

```prisma
model User {
  id        ObjectId @id @map("_id")
  email     String   @unique
  name      String?
  createdAt DateTime @default(now())
  posts     Post[]
  @@map("users")
}

model Post {
  id        ObjectId @id @map("_id")
  title     String
  content   String?
  published Boolean
  author    User     @relation(fields: [authorId], references: [id])
  authorId  ObjectId
  createdAt DateTime @default(now())
  @@map("posts")
}
```

> [!NOTE]
> On MongoDB, `@map("_id")` renames the field to `_id` everywhere: you filter on `_id`, you select `_id`, and the returned document has an `_id` key. That is why the MongoDB examples below never say `id`.

## Fetch many records or one [#fetch-many-records-or-one]

Use `.all()` when you want every matching record. It returns an array:

```typescript
const users = await db.orm.public.User.all();
```

```js no-copy
[
  { id: 'cuid20000000000000000001', email: 'alice@prisma.io', name: 'Alice', createdAt: 2026-07-06T09:03:13.808Z },
  { id: 'cuid20000000000000000002', email: 'bob@prisma.io', name: 'Bob', createdAt: 2026-07-06T09:03:14.112Z }
]
```

`.all()` takes no filter of its own. Put the filter in `.where(...)` before it. `.all()` also applies no limit, so combine it with [`limit`](#sort-and-paginate) on tables that can grow.

Use `.first()` when you want a single record. It returns the record, or `null` when nothing matches. On both databases it asks for at most one row:

```typescript
const user = await db.orm.public.User.where({ email: "alice@prisma.io" }).first();
```

```js no-copy
{ id: 'cuid20000000000000000001', email: 'alice@prisma.io', name: 'Alice', createdAt: 2026-07-06T09:03:13.808Z }
```

On PostgreSQL you can skip `.where(...)` and pass the filter straight to `.first(...)`. It takes anything `.where(...)` takes, not just the primary key. On MongoDB, `.first()` takes no argument, so filter with `.where(...)`:

  

#### PostgreSQL

```typescript
const user = await db.orm.public.User.first({ id: userId });
```

#### MongoDB

```typescript
const user = await db.orm.users.where({ _id: userId }).first();
```

If you want an error instead of `null` when nothing matches, call `.firstOrThrow()`. This is what replaces `findUniqueOrThrow` and `findFirstOrThrow`:

```typescript
const user = await db.orm.public.User.where({ email }).limit(1).all().firstOrThrow();
```

`.limit(1)` keeps it to one row. `.firstOrThrow()` throws an error with code `RUNTIME.NO_ROWS` when nothing matches. There is no `.firstOrThrow()` on `.first()`. The error is an `Error` with a `code` property, so check `"code" in error` before you read it. The same form works on MongoDB, with `db.orm.users`.

## Filter records [#filter-records]

Use `.where(...)` to narrow a query. Pass an object to match fields by equality:

```typescript
const drafts = await db.orm.public.Post.where({ published: false }).all();
```

Chain several `.where(...)` calls to combine conditions with AND. This is also how you express a range:

```typescript
const recentPosts = await db.orm.public.Post
  .where((p) => p.createdAt.gte(start))
  .where((p) => p.createdAt.lte(end))
  .all();
```

### Filter operators on PostgreSQL [#filter-operators-on-postgresql]

On PostgreSQL, `.where(...)` also accepts a callback for richer comparisons, as in the range example above. The callback receives one object, written `p` here, with a property per field of your model. Each of those fields has `.eq`, `.neq`, `.lt`, `.lte`, `.gt`, `.gte`, `.like`, `.ilike`, `.in([...])`, `.notIn([...])`, `.isNull()`, and `.isNotNull()`. `.like` and `.ilike` take SQL `LIKE` patterns, where `%` matches any run of characters:

```typescript
// Case-insensitive text search
const matchingPosts = await db.orm.public.Post
  .where((p) => p.title.ilike("%prisma%"))
  .all();

// One of several values
const team = await db.orm.public.User
  .where((u) => u.email.in(["alice@prisma.io", "bob@prisma.io"]))
  .all();
```

To combine conditions with OR, AND, or NOT, use the `or`, `and`, and `not` helpers from `@prisma/orm-postgres/orm-client`. `@prisma/orm-postgres` is already installed; it is the package `db.ts` imports from:

```typescript
import { and, not, or } from "@prisma/orm-postgres/orm-client";

const highlighted = await db.orm.public.Post
  .where((p) => or(p.title.ilike("%hello%"), p.title.ilike("%prisma%")))
  .all();

const publishedPrismaPosts = await db.orm.public.Post
  .where((p) => and(p.published.eq(true), p.title.ilike("%prisma%")))
  .all();

const notHello = await db.orm.public.Post
  .where((p) => not(p.title.eq("Hello")))
  .all();
```

### Filter operators on MongoDB [#filter-operators-on-mongodb]

On MongoDB, `.where(...)` does not take a callback. The object form covers equality:

```typescript
const drafts = await db.orm.posts.where({ published: false }).all();
```

For anything else, pass `.where(...)` a filter built with `MongoFieldFilter`, imported from `@prisma/orm-mongo/query-ast/execution`. `@prisma/orm-mongo` is the package `db.ts` imports from. `MongoFieldFilter` has one static method per operator: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `in`, `nin` (not in), `isNull`, and `isNotNull`. Each one takes the field name first:

```typescript
import { MongoFieldFilter } from "@prisma/orm-mongo/query-ast/execution";

const junePosts = await db.orm.posts
  .where(MongoFieldFilter.gte("createdAt", new Date("2026-06-01")))
  .where(MongoFieldFilter.lt("createdAt", new Date("2026-07-01")))
  .all();
```

Chained `.where(...)` calls combine with AND, the same as on PostgreSQL. Call `.not()` on a filter to invert it, and use `MongoOrExpr.of([...])` from the same import to combine filters with OR:

```typescript
import { MongoFieldFilter, MongoOrExpr } from "@prisma/orm-mongo/query-ast/execution";

const notAlice = await db.orm.users
  .where(MongoFieldFilter.eq("name", "Alice").not())
  .all();

const oldOrNew = await db.orm.posts
  .where(
    MongoOrExpr.of([
      MongoFieldFilter.eq("title", "Old"),
      MongoFieldFilter.eq("title", "New"),
    ]),
  )
  .all();
```

`MongoFieldFilter` has no case-insensitive or partial-match operator. For those, use the [pipeline builder](https://www.prisma.io/docs/orm/fundamentals/advanced-queries#mongodb-pipeline-builder), which has a `regexMatch` expression.

The full operator list is in [Filter conditions and operators](https://www.prisma.io/docs/orm/reference/orm-client#filter-conditions-and-operators).

## Select fields [#select-fields]

Use `.select(...)` to fetch only the fields you need. On PostgreSQL the result type narrows to match. On MongoDB only the fetched document narrows; the type keeps every field, so a field you left out is `undefined` at runtime. On both, you can filter on fields you did not select:

  

#### PostgreSQL

```typescript
const users = await db.orm.public.User.select("id", "email").all();
```

#### MongoDB

```typescript
const users = await db.orm.users.select("_id", "email").all();
```

```js no-copy
[
  { id: 'cuid20000000000000000001', email: 'alice@prisma.io' },
  { id: 'cuid20000000000000000002', email: 'bob@prisma.io' }
]
```

## Sort and paginate [#sort-and-paginate]

Use `.orderBy(...)` to sort, `.limit(n)` to limit, and `.offset(n)` to offset.

On PostgreSQL, sort with a callback that calls `.asc()` or `.desc()` on a field. On MongoDB, sort with MongoDB's own direction numbers: `1` for ascending and `-1` for descending.

  

#### PostgreSQL

```typescript
// Second page of posts, newest first
const page = await db.orm.public.Post
  .orderBy((p) => p.createdAt.desc())
  .limit(20)
  .offset(20)
  .all();
```

#### MongoDB

```typescript
// Second page of posts, newest first
const page = await db.orm.posts
  .orderBy({ createdAt: -1 })
  .limit(20)
  .offset(20)
  .all();
```

For a composite sort on PostgreSQL, pass an array of callbacks. Records are sorted by the first field, with the second as tiebreaker:

```typescript
const posts = await db.orm.public.Post
  .orderBy([(p) => p.createdAt.desc(), (p) => p.id.desc()])
  .all();
```

### Cursor pagination [#cursor-pagination]

Offset gets slower on deep pages. For stable pagination over a large table, follow `.orderBy(...)` with `.cursor(...)` and resume from the last record you returned. `.cursor(...)` is PostgreSQL only.

The cursor record is excluded from the next page. Pages pick up strictly after it.

Pass `.cursor(...)` a value for every field you sorted by, as in the example below. Keep the `id` tiebreaker in both the sort and the cursor. `createdAt` is not unique, and a cursor on a non-unique field alone can skip or repeat records that share the boundary value. With `id` in the cursor, pages never overlap even when timestamps tie:

```typescript
const page1 = await db.orm.public.Post
  .orderBy([(p) => p.createdAt.desc(), (p) => p.id.desc()])
  .limit(20)
  .all();

const last = page1[page1.length - 1]!;
const page2 = await db.orm.public.Post
  .orderBy([(p) => p.createdAt.desc(), (p) => p.id.desc()])
  .cursor({ createdAt: last.createdAt, id: last.id })
  .limit(20)
  .all();
```

On MongoDB, page with `.limit(n)` and `.offset(n)`.

## Count records [#count-records]

On PostgreSQL, count with `.aggregate(...)`. Like `.all()` and `.first()`, it is the last call in the chain: it says what you want back and runs the query. It takes a callback, written `a` here, and returns an object with the keys you named:

```typescript
const result = await db.orm.public.Post
  .where({ published: true })
  .aggregate((a) => ({ total: a.count() }));
```

```js no-copy
{ total: 2 }
```

The callback offers `a.count()`, `a.sum(...)`, `a.avg(...)`, `a.min(...)`, and `a.max(...)`, plus `countBigInt()`, `sumBigInt(...)`, and `avgDecimal(...)` for values beyond a JavaScript `number`. All but `a.count()` take a field name as a string, such as `a.max("createdAt")`. Ask for as many as you like in one call, one key each:

```typescript
const stats = await db.orm.public.Post
  .where({ published: true })
  .aggregate((a) => ({ total: a.count(), newest: a.max("createdAt") }));
```

There is no `.count()` method on the query chain, on either database.

MongoDB has no `.count()` and no `.aggregate(...)`. Count with the [pipeline builder](https://www.prisma.io/docs/orm/fundamentals/advanced-queries#mongodb-pipeline-builder), `db.query`: `.from("posts")` names the collection, `.count("total")` counts, `.build()` finishes the query, and `(await db.runtime()).query(...)` runs it (on MongoDB `db.runtime()` returns a promise).

```typescript
import { db } from "./prisma/db";

const built = db.query
  .from("posts")
  // .match((f) => f.published.eq(true)) counts only a subset
  .count("total")
  .build();

const [result] = await (await db.runtime()).query(built);
```

```js no-copy
{ total: 2 }
```

## Iterate a large result [#iterate-a-large-result]

You can `await` a read for an array, or loop over it with `for await`. Pick one; once you iterate a result with `for await` it is used up (see below).

`await` runs the query and gives you an array. This is the right default. You get the whole result in memory and can read the array as often as you like.

```typescript
const posts = await db.orm.public.Post.all();

console.log(posts.length);
console.log(posts[0]);
```

Use `for await` to handle records one at a time, as your loop asks for them, for example to write each one to a file:

```typescript
for await (const post of db.orm.public.Post.all()) {
  await exportToSearchIndex(post);
}
```

It does not fetch less: on the standard client (`postgres({...})` in `src/prisma/db.ts`) every row is loaded first. For large tables page with `.limit()` and `.cursor()` (see [Sort and paginate](#sort-and-paginate)).

The serverless client, `postgresServerless(...)`, does fetch rows as you iterate, but it has no `db.orm`, so none of the queries on this page run on it: see [Transactions and runtime](https://www.prisma.io/docs/orm/reference/transactions-and-runtime).

### An iterated result can only be read once [#an-iterated-result-can-only-be-read-once]

Once a `for await` loop has touched a result, that result is finished, even if the loop exited early. Iterating it again, or `await`ing it afterwards, throws:

```typescript
const result = db.orm.public.Post.all();

for await (const post of result) {
  // ...
}

await result;
```

```text no-copy
RuntimeError: AsyncIterableResult iterator has already been consumed via for-await loop.
Each AsyncIterableResult can only be iterated once.
```

`AsyncIterableResult` is the type a query returns before you await it; the error means you read it twice. The error has the code `RUNTIME.ITERATOR_CONSUMED`.

If you need the data more than once, `await` the query into an array and reuse the array:

```typescript
const posts = await db.orm.public.Post.all();

const published = posts.filter((p) => p.published);
const titles = posts.map((p) => p.title);
```

The read-once rule is the same on PostgreSQL and MongoDB.

## Common mistakes [#common-mistakes]

### Fetching everything to use one record [#fetching-everything-to-use-one-record]

You wanted one record, so you queried and took the first element:

```typescript
const users = await db.orm.public.User.where({ email }).all();
const user = users[0];
```

This fetches every matching record and throws away the rest. Use `.first()` instead. It returns one record or `null`, and it asks the database for at most one row:

```typescript
const user = await db.orm.public.User.where({ email }).first();
```

### Forgetting that .all() has no limit [#forgetting-that-all-has-no-limit]

`.all()` returns every match. On a table that grows, yesterday's fast query becomes today's slow one. Add `.limit(n)` when you don't genuinely need every record. When you do need every record, page through the table: `.cursor(...)` on PostgreSQL, `.offset(n)` on MongoDB.

### Reusing an iterated result [#reusing-an-iterated-result]

You read a result with `for await`, then tried to read it again. The second read throws, because a result is used up as it is iterated. Store the data if you need it twice:

```typescript
const posts = await db.orm.public.Post.all();
// posts is a plain array now; read it as often as you like
```

## Prompt your coding agent [#prompt-your-coding-agent]

Projects created with `npm create prisma@latest` include the Prisma ORM skills for your coding agent; in an existing project, run `npx prisma skills sync`. Skills are instruction files a coding agent reads, and the [`prisma-8` skill](https://www.prisma.io/docs/ai/tools/skills#available-skills-for-prisma-8) covers everything on this page. Prompts that map to each section:

* "Using the prisma-8 skill, write a query that returns the 20 newest published posts."
* "Add a case-insensitive title search to the posts query, using the .ilike operator."
* "Convert this offset pagination to cursor pagination with the .cursor() API."
* "This export loops over a huge table. Rewrite it to page through the rows with .limit() and .cursor()."

## Next [#next]

* [Write data](https://www.prisma.io/docs/orm/fundamentals/writing-data): create, update, delete, and upsert records.
* [Read related records](https://www.prisma.io/docs/orm/fundamentals/relations-and-joins) in the same query with `.include(...)`.
* [Use advanced queries](https://www.prisma.io/docs/orm/fundamentals/advanced-queries) when a shape needs the SQL query builder or a MongoDB pipeline.

## Related pages

- [`Advanced queries`](https://www.prisma.io/docs/orm/fundamentals/advanced-queries): Use the SQL query builder on PostgreSQL and the pipeline builder on MongoDB for queries the ORM API can't express.
- [`Relations and joins`](https://www.prisma.io/docs/orm/fundamentals/relations-and-joins): Read related records in one query with .include(), and understand how one-to-one, one-to-many, and many-to-many relationships work.
- [`Transactions`](https://www.prisma.io/docs/orm/fundamentals/transactions): Run several writes so they all succeed or all fail together with db.transaction().
- [`Writing data`](https://www.prisma.io/docs/orm/fundamentals/writing-data): Create, update, delete, and upsert records with Prisma ORM, one at a time or in bulk.