# 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, and the last call in the chain says what you want back and runs the query, usually `.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, while `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`, and `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, but `.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 Date posts Post[] @@map("users") } model Post { id ObjectId @id @map("_id") title String content String? published Bool author User @relation(fields: [authorId], references: [id]) authorId ObjectId createdAt Date @@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, so 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, and 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(...)`, which 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()`, the replacement for `findUniqueOrThrow` and `findFirstOrThrow`: ```typescript const user = await db.orm.public.User.where({ email }).limit(1).all().firstOrThrow(); ``` `.limit(1)` keeps it to one row, and `.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, and 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, which 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()`, as far as its type supports them: a native enum field (`pg.enum(...)`), for example, has no `.like` or `.ilike`. `.like` and `.ilike` take SQL `LIKE` patterns, where `%` matches any run of characters. For search over words rather than patterns, text fields also have PostgreSQL [full-text search](https://www.prisma.io/docs/orm/reference/orm-client#full-text-search): ```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, but 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`, and 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(); ``` Each value goes through the field's type, as in a write, so pass the value your code reads from that field. For an `Int64` field that is a `bigint`, which a filter takes wrapped in a `MongoParamRef` from `@prisma/orm-mongo/value`, as in `MongoFieldFilter.gt("views", new MongoParamRef(5n))`. A value the field's type refuses throws an error whose `code` is `RUNTIME.ENCODE_FAILED` before the query runs. 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, while 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, and 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, and 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(); ``` On PostgreSQL, a sort can also use a related record, a count, and a placement for nulls: ```typescript // Posts by their author's name const byAuthor = await db.orm.public.Post .orderBy([(p) => p.author.name.asc(), (p) => p.id.asc()]) .all(); // Users by how many posts they have const mostPosts = await db.orm.public.User .orderBy((u) => u.posts.count().desc()) .all(); // Posts without content go last const titled = await db.orm.public.Post .orderBy((p) => p.content.desc({ nulls: "last" })) .all(); ``` A relation to one record gives you that record's fields, one relation deep. A relation to many records gives you `count()`, which takes an optional filter, as in `u.posts.count((p) => p.published.eq(true))`. The [`orderBy()` reference](https://www.prisma.io/docs/orm/reference/orm-client#orderby) has the details. ### Cursor pagination [#cursor-pagination] Offset gets slower on deep pages, so 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, so 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, because `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(); ``` A cursor works only when every sort is a plain field of the model. With a sort by a related record, a count, an extension operation such as a vector distance, or a `nulls` placement, `.cursor(...)` throws an error whose `code` is `ORM.ARGUMENT_INVALID`, so page those queries with `.limit(n)` and `.offset(n)`. 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, which 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)). A client created with the `cursor` option, such as `postgres({ contractJson, url, cursor: { batchSize: 100 } })`, reads the rows through a server-side cursor instead, in batches, as your loop asks for them. See [`postgres(options)`](https://www.prisma.io/docs/orm/reference/transactions-and-runtime#postgresoptions). The queries on this page also run on the serverless client from `@prisma/orm-postgres/serverless`, through the connection that its `connect({ url })` returns. Its reads load every row first too, unless you create it with `cursor`. See [Serverless client on PostgreSQL](https://www.prisma.io/docs/orm/reference/transactions-and-runtime#serverless-client-on-postgresql). ### 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, so 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, and 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, and in an existing project you run `npx prisma skills sync` to add them. 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, so try 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.