# Selecting fields

> where vs select vs include - which rows qualify, and which fields survive.

URL: https://better-drizzle.com/docs/querying/selecting-fields

Three options control a query's result, and they solve different problems:

| Goal                                    | Use                               |
| --------------------------------------- | --------------------------------- |
| "Only rows that match these conditions" | [`where`](/docs/querying/filters) |
| "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



**better-drizzle**

```ts  
await client.posts.findMany({
	where: { published: true },
});
```

**Drizzle**

```ts  
import { eq } from 'drizzle-orm';

await db.select().from(posts).where(eq(posts.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:



**better-drizzle**

```ts  
const rows = await client.posts.findMany({
	select: {
		id: true,
		title: true,
	},
});
//    ^? { id: number; title: string }[]
```

**Drizzle**

```ts  
const rows = await db
	.select({ id: posts.id, title: posts.title })
	.from(posts);
```

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:



**better-drizzle**

```ts  
const posts = await client.posts.findMany({
	include: { author: true },
});
//    ^? (Post & { author: User | null })[]
```

**Drizzle**

```ts  
import { eq, getColumns } from 'drizzle-orm';

const rows = await db
	.select({ ...getColumns(posts), author: getColumns(users) })
	.from(posts)
	.leftJoin(users, eq(posts.authorId, users.id));
```

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`:



**better-drizzle**

```ts  
const posts = await client.posts.findMany({
	select: {
		id: true,
		title: true,
		author: {
			select: { id: true, name: true },
		},
	},
});
```

**Drizzle**

```ts  
import { eq } from 'drizzle-orm';

const rows = await db
	.select({
		id: posts.id,
		title: posts.title,
		author: { id: users.id, name: users.name },
	})
	.from(posts)
	.leftJoin(users, eq(posts.authorId, users.id));
```

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.
