Querying

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:

GoalUse
"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 }[]

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.

On this page