better-drizzle

Create, update & delete

Insert, change, and remove rows with create, createMany, update, updateMany, delete, and deleteMany.

Use these methods for everyday writes. Single-row methods return the affected row (or null); batch methods return { count, data? }. For per-row values in one statement see updateEach, and for create-or-update see upsert.

create

Inserts one row and returns the created record:

const user = await client.users.create({
	data: {
		email: 'alice@example.com',
		name: 'Alice',
		active: true,
	},
});

createMany

Inserts many rows in one statement and returns { count, data? }:

const {
	count, // number inserted
	data, // inserted rows, when the driver supports RETURNING
} = await client.users.createMany({
	data: [
		{ email: 'a@example.com', name: 'A', active: true },
		{ email: 'b@example.com', name: 'B', active: false },
	],
});

Skip duplicates

create and createMany accept skipDuplicates: true, or the column keys of the conflict target:

const maybeCreated = await client.users.create({
	data: {
		email: 'alice@example.com',
		name: 'Alice',
		active: true,
	},
	skipDuplicates: ['email'],
});

With single-row create, a skipped insert returns null. With createMany, count only includes rows that were actually inserted.

Insert in chunks

Added in 0.3.0

For large inputs, batchSize splits data into one INSERT per chunk so a single statement stays under driver parameter limits. count sums the inserted rows and data keeps input order. Hooks and plugins still run once per call. Chunks run one after another without an implicit transaction, the same as upsertMany; wrap the call in db.transaction(...) when all chunks must commit together. batchSize must be a positive integer.

const { count } = await client.users.createMany({
	data: rows,
	batchSize: 500,
	skipDuplicates: true,
});

update

Updates at most one matching row. The result is nullable (the target may not exist) and also exposes .throw().

When where matches several rows, only one of them is updated, on every dialect. Use updateMany to update every match.

const updated = await client.users.update({
	where: { id: 1 },
	data: { name: 'Alice Updated' },
});

if (!updated) {
	throw new Error('Update target not found');
}

An update whose data resolves to no values ({}, or every field undefined after plugins run) skips the UPDATE and returns the current row, or null when nothing matches.

updateMany

Added in 0.3.0

Updates every matching row and returns { count, data? }. data holds the updated rows:

const { count, data } = await client.users.updateMany({
	where: { active: true },
	data: { active: false },
}); // count: rows updated

PostgreSQL and SQLite return complete updated rows in data by default using native RETURNING. Pass a scalar select to narrow those rows, for example select: { id: true, active: true }. Relation projections are unsupported. When no row matches, the result is { count: 0 } with no data; MySQL always returns only { count }.

delete

Deletes at most one matching row and returns it (nullable, with .throw()). When where matches several rows, only one is deleted; use deleteMany to delete every match:

const deleted = await client.users.delete({
	where: { id: 1 },
});

deleteMany

Deletes every matching row and returns { count, data? }:

const { count, data } = await client.users.deleteMany({
	where: { active: false },
}); // count: rows deleted

On PostgreSQL and SQLite, data contains the deleted rows, or the scalar fields requested by select, from native RETURNING. Empty results omit data. MySQL returns only { count }. Relation projections are unsupported.

Write relations

create, update, and upsert accept connect, disconnect, and set inside data, so you attach related rows without resolving foreign keys yourself:

const post = await client.posts.create({
	data: {
		title: 'Connected post',
		author: { connect: { email: 'alice@example.com' } },
	},
});

See relation writes for every command, its rules, and the raw Drizzle equivalent.

Project write results

create, update, delete, upsert, createMany, updateEach, and upsertMany can project results. updateEach and upsertMany support select; the others support select and include:

const user = await client.users.create({
	data: { email: 'writer@example.com', name: 'Writer', active: true },
	select: { id: true, email: true },
});
//    ^? { id: number; email: string }

Edge cases

Reference

MethodWritesReturnsselect / include
createone rowthe row, or null when skipDuplicates skips itboth
createManymany rows{ count, data? }both
updateat most one rowthe row or null, with .throw()both
updateManyevery match{ count, data? }scalar select
updateEachlisted rows, per-row values{ count, data? }scalar select
deleteat most one rowthe row or null, with .throw()both
deleteManyevery match{ count, data? }scalar select
upsertone rowthe rowboth
upsertManymany rows{ count, data? }scalar select

On this page