Entity Hooks
Overview
Entities can implement lifecycle hooks that are automatically called during CRUD operations. This enables automatic behavior like validation, timestamp management, and data transformation.
Available Hooks
type ValidationHook interface { Validate(ctx context.Context) error }
type BeforeCreateHook interface { BeforeCreate(ctx context.Context) error }
type AfterCreateHook interface { AfterCreate(ctx context.Context) error }
type BeforeUpdateHook interface { BeforeUpdate(ctx context.Context) error }
type AfterUpdateHook interface { AfterUpdate(ctx context.Context) error }
type BeforeDeleteHook interface { BeforeDelete(ctx context.Context) error }
type AfterDeleteHook interface { AfterDelete(ctx context.Context) error }
type BeforeFindHook interface { BeforeFind(ctx context.Context) error }
type AfterFindHook interface { AfterFind(ctx context.Context) error }Execution Order
When performing a create or update operation, hooks run in this order:
Validate()— validate the entity before anything elseBeforeCreate()/BeforeUpdate()— transform or enrich data- Database operation — the actual insert/update
AfterCreate()/AfterUpdate()— post-operation logic
For deletes:
BeforeDelete()- Database operation
AfterDelete()
For finds:
BeforeFind()- Database query
AfterFind()(called for each loaded entity)
Examples
Validation Hook
type User struct {
ID int
Email string
Name string
}
func (u *User) Validate(ctx context.Context) error {
if u.Email == "" {
return gpa.NewError(gpa.ErrorTypeValidation, "email is required")
}
if u.Name == "" {
return gpa.NewError(gpa.ErrorTypeValidation, "name is required")
}
return nil
}Before Create Hook
func (u *User) BeforeCreate(ctx context.Context) error {
u.Email = strings.ToLower(strings.TrimSpace(u.Email))
u.CreatedAt = time.Now()
u.UpdatedAt = time.Now()
return nil
}After Create Hook
func (u *User) AfterCreate(ctx context.Context) error {
// Send welcome email, log audit, etc.
return sendWelcomeEmail(u.Email)
}Before Update Hook
func (u *User) BeforeUpdate(ctx context.Context) error {
u.UpdatedAt = time.Now()
return nil
}After Find Hook
func (u *User) AfterFind(ctx context.Context) error {
// Compute derived fields, mask sensitive data, etc.
u.DisplayName = fmt.Sprintf("%s (%s)", u.Name, u.Email)
return nil
}Full Example
type Task struct {
ID uint
Title string
Description string
Status string
CreatedAt time.Time
UpdatedAt time.Time
}
func (t *Task) Validate(ctx context.Context) error {
if t.Title == "" {
return gpa.NewError(gpa.ErrorTypeValidation, "title is required")
}
return nil
}
func (t *Task) BeforeCreate(ctx context.Context) error {
if t.Status == "" {
t.Status = "todo"
}
t.CreatedAt = time.Now()
t.UpdatedAt = time.Now()
return nil
}
func (t *Task) BeforeUpdate(ctx context.Context) error {
t.UpdatedAt = time.Now()
return nil
}Provider-Specific Hook Support
| Provider | Validation | Before/After Create | Before/After Update | Before/After Delete | After Find |
|---|---|---|---|---|---|
| GORM | Yes | Yes | Yes | Yes | Yes |
| Bun | No | Yes | Yes | Yes | Yes |
| MongoDB | No | No | No | No | No |
| Redis* | No | Yes (Set) | No | Yes (Delete) | Yes |
*Redis hooks apply to SetWithTTL and DeleteKey operations only.