Skip to main content
Hooks are methods defined on your model struct that GORM calls automatically at specific points in the lifecycle of a database operation. You can use them to enforce business rules, update related data, or abort an operation by returning an error.

Available hooks

GORM supports hooks for create, save, update, delete, and query operations.
Executed in this order:
  1. BeforeSave
  2. BeforeCreate
  3. create record in database
  4. AfterCreate
  5. AfterSave
Executed in this order:
  1. BeforeSave
  2. BeforeUpdate
  3. update record in database
  4. AfterUpdate
  5. AfterSave
Executed in this order:
  1. BeforeDelete
  2. delete record from database
  3. AfterDelete
Executed after the query:
  1. AfterFind

Defining a hook

Define a hook as a pointer-receiver method on your model struct. The method must accept *gorm.DB and return error.
type User struct {
    gorm.Model
    Name  string
    Email string
    Role  string
}

func (u *User) BeforeCreate(tx *gorm.DB) error {
    if u.Role == "" {
        u.Role = "member"
    }
    return nil
}

func (u *User) AfterCreate(tx *gorm.DB) error {
    // Use tx for additional queries within the same transaction
    return tx.Model(u).Update("created_by", "system").Error
}

Aborting an operation

Return a non-nil error from any hook to abort the operation. GORM rolls back the transaction and propagates the error to the caller.
func (u *User) BeforeDelete(tx *gorm.DB) error {
    if u.Role == "admin" {
        return errors.New("admin accounts cannot be deleted")
    }
    return nil
}
result := db.Delete(&user)
if result.Error != nil {
    // hook aborted the delete
    log.Println(result.Error)
}

Using tx inside hooks

The tx parameter inside a hook shares the same transaction as the triggering operation. Use it to perform additional queries that must succeed or fail together with the main operation.
func (u *User) AfterSave(tx *gorm.DB) error {
    return tx.Create(&AuditLog{
        UserID: u.ID,
        Action: "save",
    }).Error
}
Do not use the global db variable inside a hook. Always use the tx parameter to stay within the current transaction.

All hook signatures

// BeforeCreate called before inserting a record
func (u *User) BeforeCreate(tx *gorm.DB) error

// AfterCreate called after inserting a record
func (u *User) AfterCreate(tx *gorm.DB) error

// BeforeSave called before Create or Update
func (u *User) BeforeSave(tx *gorm.DB) error

// AfterSave called after Create or Update
func (u *User) AfterSave(tx *gorm.DB) error

// BeforeUpdate called before updating a record
func (u *User) BeforeUpdate(tx *gorm.DB) error

// AfterUpdate called after updating a record
func (u *User) AfterUpdate(tx *gorm.DB) error

// BeforeDelete called before deleting a record
func (u *User) BeforeDelete(tx *gorm.DB) error

// AfterDelete called after deleting a record
func (u *User) AfterDelete(tx *gorm.DB) error

// AfterFind called after a query (Find, First, Last, etc.)
func (u *User) AfterFind(tx *gorm.DB) error

Skipping hooks

Create a session with SkipHooks: true to bypass all hooks for that operation.
db.Session(&gorm.Session{SkipHooks: true}).Create(&user)
db.Session(&gorm.Session{SkipHooks: true}).Delete(&user)
UpdateColumn and UpdateColumns skip hooks by default — they bypass the callback pipeline entirely and write directly to the database.

Registering callbacks programmatically

Beyond model methods, you can register callbacks globally on the db instance using the callback manager. This is useful for cross-cutting concerns like auditing or tracing.
// Register a callback that runs before every create operation
db.Callback().Create().Before("gorm:create").Register("audit:before_create", func(db *gorm.DB) {
    if db.Statement.Schema != nil {
        fmt.Printf("creating record in table: %s\n", db.Statement.Table)
    }
})

// Remove an existing callback
db.Callback().Delete().Remove("gorm:delete")

// Replace an existing callback with a custom implementation
db.Callback().Update().Replace("gorm:update", myCustomUpdateFunc)
Callback processors support Before, After, Match, Register, Remove, and Replace methods for fine-grained control over execution order.

Build docs developers (and LLMs) love