Insert & Upsert
Single insert, bulk insert, and upsert (ON CONFLICT) — all through the InsertBuilder. These are PostgreSQL-native operations, not hacked abstractions.
Two ways to insert
For simple single-record inserts, use db.user.create(). For bulk inserts, upserts (ON CONFLICT), or shaped RETURNING, use the InsertBuilder via db.user.insert().
create() — simple
- ·Single record only
- ·Always returns full record
- ·No conflict handling
- ·Simplest API
insert() — powerful
- ·Single or bulk insert
- ·ON CONFLICT (upsert)
- ·Shaped RETURNING
- ·InsertBuilder pattern
Single insert
const user = await db.user .insert({ email: '[email protected]', name: 'Alice', role: 'USER' }) .execute();// Returns the created record (RETURNING *)Bulk insert
Insert multiple rows in a single SQL statement. This is much faster than inserting one at a time — one round trip instead of N.
const users = await db.user.insert([ { email: '[email protected]', name: 'Alice', role: 'USER' }, { email: '[email protected]', name: 'Bob', role: 'ADMIN' }, { email: '[email protected]', name: 'Charlie', role: 'USER' },]).executeMany(); // Generated SQL:// INSERT INTO "users" ("email", "name", "role")// VALUES ($1, $2, $3), ($4, $5, $6), ($7, $8, $9)// RETURNING *Upsert (ON CONFLICT DO UPDATE)
Insert a record, but if it conflicts with a unique constraint, update instead. This is PostgreSQL's native upsert — atomic, race-condition-free, and efficient. No "check if exists then insert or update" pattern needed.
await db.user .insert({ email: '[email protected]', name: 'Alice' }) .onConflict({ columns: ['email'], // which unique constraint to check action: 'update', // what to do on conflict set: { name: 'Alice Updated' }, // which fields to update }) .execute(); // Generated SQL:// INSERT INTO "users" ("email", "name")// VALUES ($1, $2)// ON CONFLICT ("email") DO UPDATE SET "name" = $3// RETURNING *ON CONFLICT DO NOTHING
Skip the insert silently if it would conflict. Useful for idempotent operations — "insert this if it doesn't exist, otherwise do nothing."
await db.user .insert({ email: '[email protected]', name: 'Alice' }) .onConflict({ columns: ['email'], action: 'nothing' }) .execute(); // If email '[email protected]' already exists → nothing happens// If it doesn't exist → record is insertedShaped RETURNING
By default, RETURNING * returns all columns. Use .returning()to specify exactly which columns you want back — useful when you only need the generated ID and don't want to transfer the entire record.
const result = await db.user .insert({ email: '[email protected]', name: 'X' }) .returning(['id', 'email']) .execute(); // result: { id: '...', email: '[email protected]' } — no name, no timestamps // Or return everything:.returning('*') // this is the defaultMethod reference
| Method | Description |
|---|---|
db.model.insert(data) | Start an insert operation (single or array) |
.onConflict({ columns, action, set? }) | Handle unique constraint conflicts |
.returning(cols) | Specify which columns to return (default: '*') |
.execute() | Execute and return first row (single insert) |
.executeMany() | Execute and return all rows (bulk insert) |
.toSQL() | Compile to { sql, params } without executing |