Reference

Model delegate

Every method on a table delegate — reads, writes, pagination, and the plugin-related helpers.

A model delegate is what you get from client.users, client.posts, or client.repository(name). Every table has the same surface, typed against its own columns and relations.

Reads

MethodSignatureReturns
findMany(args?: QueryArgs)Row[]
findFirst(args?: QueryArgs)ThrowingResult<Row>
findOne(args?: QueryArgs)ThrowingResult<Row> (alias of findFirst)
findUnique(args: QueryArgs)ThrowingResult<Row>
count(args?: CountArgs)number
exists(args?: ExistsArgs)boolean
paginate(args: PaginationArgs)PaginationResult<Row>

ThrowingResult<T> is a Promise<T | null> with a .throw() method — see throwing results. All read args are documented in query options.

Writes

MethodSignatureReturns
create(args: { data, skipDuplicates?, select?, include?, meta? })Row | null when skipDuplicates skips
createMany(args: { data: Row[], skipDuplicates?, select?, include?, meta? })BatchResult<Row>
update(args: { where, data, select?, include?, meta? })ThrowingResult<Row>
updateMany(args: { where?, data, meta? })BatchResult<never>
upsert(args: { where, create, update, select?, include?, meta? })Row
upsertMany(args: { data, target, update, select?, batchSize?, where?, meta? })BatchResult<Row>
delete(args: { where, select?, include?, meta? })ThrowingResult<Row>
deleteMany(args: { where?, meta? })BatchResult<never>

BatchResult<T> is { count: number; data?: T[] }data is populated when the driver supports RETURNING.

Less obvious write options

skipDuplicates

create and createMany accept skipDuplicates.

  • true ignores duplicate conflicts using the dialect's supported default conflict handling
  • ['email'] or another explicit column list targets specific unique columns when the dialect supports it
const maybeUser = await client.users.create({
	data: {
		email: 'alice@example.com',
		name: 'Alice',
	},
	skipDuplicates: true,
});

if (!maybeUser) {
	// insert was skipped
}

When a single-row create is skipped, it returns null. For createMany, count reflects only rows that were actually inserted.

upsertMany

upsertMany is the native batch upsert path for high-volume writes. It requires an explicit conflict target and supports multiple update strategies:

  • 'all' updates every mutable column on conflict
  • ['name', 'active'] updates only the listed columns
  • { name: 'Alice', updatedAt: sql\now()` }` provides an explicit update object
  • (ctx) => ({ name: ctx.excluded.name }) builds the update object from excluded, sql, and table columns
import { sql } from 'drizzle-orm';

const result = await client.users.upsertMany({
	data: [
		{ email: 'a@example.com', name: 'A', active: true },
		{ email: 'b@example.com', name: 'B', active: false },
	],
	target: ['email'],
	update: (ctx) => ({
		name: ctx.excluded.name,
		active: ctx.excluded.active,
		updatedAt: sql`now()`,
	}),
	batchSize: 500,
	select: {
		id: true,
		email: true,
		name: true,
	},
});

Constraints worth knowing:

  • upsertMany is native-only and intentionally fails fast on unsupported dialects/features
  • where is SQL-only and applies to the update side of the conflict path
  • select is supported, but relation selects are not
  • include is intentionally not supported

Plugin helpers

These exist on every delegate and are mostly used by plugins:

MemberDescription
$model{ name, dbName, hasColumn(column) } metadata for the table
$statethe current ephemeral plugin state
$withState(state)clone the delegate with merged plugin state
$withoutPlugins()clone the delegate with all plugin transforms/hooks bypassed

Plugins can also add methods here — for example the soft-delete plugin adds restore() and restoreById() to compatible models.

$withState() and $withoutPlugins() are not just for plugin authors. They are also useful in application code when you need a one-off state flag or an intentional bypass for repair/admin flows.

Args are extensible

Plugins that declare operationArgs add typed fields to these method signatures. For example, with the soft-delete plugin, findMany accepts a deleted: 'with' | 'without' | 'only' argument, and delete accepts mode: 'soft' | 'hard'.

On this page