Selecting fields
where vs select vs include - which rows qualify, and which fields survive.
Three options control a query's result, and they solve different problems:
| Goal | Use |
|---|---|
| "Only rows that match these conditions" | where |
| "Only these scalar and relation fields" | select |
| "The full row, plus these relations" | include |
where filters rows. select and include shape the payload - and the return type is inferred from whichever you use.
where filters rows
await client.posts.findMany({
where: { published: true },
});Without select or include, you get the full row for each table - all scalar columns, no relations.
select narrows the payload
select lists exactly the fields you want. Anything not listed is dropped, and the return type narrows to match:
const rows = await client.posts.findMany({
select: {
id: true,
title: true,
},
});
// ^? { id: number; title: string }[]Unknown select keys throw at runtime, including keys set to false or undefined. This also applies to nested projections. Use only declared columns and relations. Batch write projections (updateMany, deleteMany, updateEach, upsertMany) accept scalar columns only.
Reach for select when:
- you want to reduce payload size
- the response goes straight to an API boundary
- you do not want to accidentally leak extra columns
include keeps the row and adds relations
include keeps every scalar column and attaches the relations you name:
const posts = await client.posts.findMany({
include: { author: true },
});
// ^? (Post & { author: User | null })[]Reach for include when a handler wants the model plus related records.
Pick one
select and include are mutually exclusive on a single query level - use
select to narrow, or include to keep-and-extend. (You can still narrow a
relation inside an include; see below.)
Projecting relations
Both select and include can shape a relation by giving it a nested query instead of true:
const posts = await client.posts.findMany({
select: {
id: true,
title: true,
author: {
select: { id: true, name: true },
},
},
});The nested object accepts the same where, select, orderBy, and take you would use on a top-level query - so you can filter and order related rows too.
A note on API boundaries
At API boundaries, prefer explicit shapes. Returning large default payloads "because it was easy" is how extra columns leak into responses. A narrow select (or a small include) keeps the contract obvious and the payload small.