Model and query hooks

Introduction

Hooks are user-defined functions that are called before and/or after certain operations, for example, before every processed query.

To ensure that your model implements a hook interface, use compile time checksHooks - 图1open in new window, for example, var _ bun.QueryHook = (*MyHook)(nil).

Disclaimer

It may sound like a good idea to use hooks for validation or caching because this way you can’t forget to sanitize data or check permissions. It gives false sense of safety.

Don’t do that. Code that uses hooks is harder to read, understand, and debug. It is more complex and error-prone. Instead, prefer writing simple code like thisHooks - 图2open in new window even if that means repeating yourself:

  1. func InsertUser(ctx context.Context, db *bun.DB, user *User) error {
  2. // before insert
  3. if _, err := db.NewInsert().Model(user).Exec(ctx); err != nil {
  4. return err
  5. }
  6. // after insert
  7. return nil
  8. }

Model hooks

BeforeAppendModel

To update certain fields before inserting or updating a model, use bun.BeforeAppendModelHook which is called just before constructing a query. For exampleHooks - 图3open in new window:

  1. type Model struct {
  2. ID int64
  3. CreatedAt time.Time
  4. UpdatedAt time.Time
  5. }
  6. var _ bun.BeforeAppendModelHook = (*Model)(nil)
  7. func (m *Model) BeforeAppendModel(ctx context.Context, query bun.Query) error {
  8. switch query.(type) {
  9. case *bun.InsertQuery:
  10. m.CreatedAt = time.Now()
  11. case *bun.UpdateQuery:
  12. m.UpdatedAt = time.Now()
  13. }
  14. return nil
  15. }

Before/AfterScanRow

Bun also calls BeforeScanRow and AfterScanRow hooks before and after scanning row values. For exampleHooks - 图4open in new window:

  1. type Model struct{}
  2. var _ bun.BeforeScanRowHook = (*Model)(nil)
  3. func (m *Model) BeforeScanRow(ctx context.Context) error { return nil }
  4. var _ bun.AfterScanRowHook = (*Model)(nil)
  5. func (m *Model) AfterScanRow(ctx context.Context) error { return nil }

Model query hooks

You can also define model query hooks that are called before and after executing certain type of queries on a certain model. Such hooks are called once for a query and using a nil model. To access the query data, use query.GetModel().Value().

  1. var _ bun.BeforeSelectHook = (*Model)(nil)
  2. func (*Model) BeforeSelect(ctx context.Context, query *bun.SelectQuery) error { return nil }
  3. var _ bun.AfterSelectHook = (*Model)(nil)
  4. func (*Model) AfterSelect(ctx context.Context, query *bun.SelectQuery) error { return nil }
  5. var _ bun.BeforeInsertHook = (*Model)(nil)
  6. func (*Model) BeforeInsert(ctx context.Context, query *bun.InsertQuery) error { nil }
  7. var _ bun.AfterInsertHook = (*Model)(nil)
  8. func (*Model) AfterInsert(ctx context.Context, query *bun.InsertQuery) error { return nil }
  9. var _ bun.BeforeUpdateHook = (*Model)(nil)
  10. func (*Model) BeforeUpdate(ctx context.Context, query *bun.UpdateQuery) error { return nil }
  11. var _ bun.AfterUpdateHook = (*Model)(nil)
  12. func (*Model) AfterUpdate(ctx context.Context, query *bun.UpdateQuery) error { return nil }
  13. var _ bun.BeforeDeleteHook = (*Model)(nil)
  14. func (*Model) BeforeDelete(ctx context.Context, query *bun.DeleteQuery) error { return nil }
  15. var _ bun.AfterDeleteHook = (*Model)(nil)
  16. func (*Model) AfterDelete(ctx context.Context, query *bun.DeleteQuery) error { return nil }
  17. var _ bun.BeforeCreateTableHook = (*Model)(nil)
  18. func (*Model) BeforeCreateTable(ctx context.Context, query *CreateTableQuery) error { return nil }
  19. var _ bun.AfterCreateTableHook = (*Model)(nil)
  20. func (*Model) AfterCreateTable(ctx context.Context, query *CreateTableQuery) error { return nil }
  21. var _ bun.BeforeDropTableHook = (*Model)(nil)
  22. func (*Model) BeforeDropTable(ctx context.Context, query *DropTableQuery) error { return nil }
  23. var _ bun.AfterDropTableHook = (*Model)(nil)
  24. func (*Model) AfterDropTable(ctx context.Context, query *DropTableQuery) error { return nil }

Query hooks

Bun supports query hooks which are called before and after executing a query. Bun uses query hooks for logging queries and for tracing.

  1. type QueryHook struct{}
  2. func (h *QueryHook) BeforeQuery(ctx context.Context, event *bun.QueryEvent) context.Context {
  3. return ctx
  4. }
  5. func (h *QueryHook) AfterQuery(ctx context.Context, event *bun.QueryEvent) {
  6. fmt.Println(time.Since(event.StartTime), string(event.Query))
  7. }
  8. db.AddQueryHook(&QueryHook{})