Create, Update & Destroy
Creating records
Model.create(attrs) — insert and return
ts
// schema.ts
export const users = pgTable('users', {
id: integer('id').primaryKey().generatedAlwaysAsIdentity(),
email: text('email').notNull(),
name: text('name').notNull(),
role: integer('role').notNull().default(0),
})ts
// models/User.model.ts
@model('users')
export class User extends ApplicationRecord {
static role = Attr.enum({ customer: 0, admin: 1 } as const)
}ts
const user = await User.create({ email: 'alice@example.com', name: 'Alice', role: 'admin' })
user.id // auto-assigned from the DB
user.role // → 'admin'
// Validation errors raise instead of returning
try {
await User.create({ email: '' })
} catch (e) {
// ValidationError with e.errors
}new + save()
Useful when you need to set properties conditionally:
ts
const user = new User()
user.email = 'bob@example.com'
user.name = 'Bob'
await user.save()
user.isNewRecord // false after save
user.id // set by DBnew → isNewRecord
A record is a new record (not yet saved) if it was constructed with new Model() or the first argument is not true:
ts
const u = new User({ email: 'x@x.com', name: 'X' })
u.isNewRecord // true
await u.save()
u.isNewRecord // falseUpdating records
instance.update(attrs) — partial update
ts
const user = await User.find(1)
await user.update({ name: 'Alice Smith' })
// Only the name column is updatedinstance.save() — persist all changes
ts
user.name = 'Alice Smith'
user.role = 'admin'
await user.save()
// UPDATE users SET name = ?, role = ? WHERE id = ?
// Only changed columns are sent (dirty tracking)Relation.updateAll(attrs) — bulk update
ts
await User.where({ role: 'customer' }).updateAll({ role: 'guest' })
// UPDATE users SET role = 0 WHERE role = 0No lifecycle hooks are run for updateAll — it's a raw SQL UPDATE for performance.
Destroying records
instance.destroy()
ts
const user = await User.find(1)
await user.destroy()
user.isDestroyed // trueRuns @beforeDestroy and @afterDestroy hooks. If the model has dependent: 'destroy' associations, those records are destroyed first.
Relation.destroyAll() — bulk destroy
Loads each record and calls destroy() on it (hooks run):
ts
await User.where({ active: false }).destroyAll()Relation.deleteAll() — raw DELETE (no hooks)
ts
await User.where({ active: false }).deleteAll()
// DELETE FROM users WHERE active = false — no hooks, fasterreload()
Refreshes the record from the database, discarding any unsaved changes:
ts
user.name = 'Temporary Name'
await user.reload()
user.name // original name from DBtouch()
Updates the updatedAt timestamp (and nothing else):
ts
await user.touch()
// UPDATE users SET updated_at = NOW() WHERE id = ?The save pipeline
When you call save() or create(), ActiveDrizzle runs this sequence:
- Run
@beforeSavehooks (and@beforeCreate/@beforeUpdate) - Run
@validateand@serverValidatehooks — abort if errors - Coerce values via
Attr.set - INSERT or UPDATE in the database
- Run
@afterSavehooks (and@afterCreate/@afterUpdate) - (If inside a transaction) queue
@afterCommithooks; flush when the outermost transaction commits - Autosave any associations with
autosave: true
isValid() / errors
Run validations without saving:
ts
const user = new User({ email: '' })
const valid = await user.isValid() // → false
user.errors // { email: ['is required'] }