Skip to main content

Lifecycle Hooks

FireODM models support lifecycle callbacks (hooks) that run before or after various operations. Use these to:

  • Set default values or timestamps
  • Enforce business rules
  • Trigger side effects (logging, analytics)

Supported Hooks

HookWhen It RunsSignature
beforeSaveBefore .save() (create or overwrite)async beforeSave(options?: SetOptions)
afterSaveAfter .save()async afterSave(result: WriteResult)
beforeUpdateBefore .update()async beforeUpdate(data: UpdateData<T>)
afterUpdateAfter .update()async afterUpdate(result: WriteResult, data: UpdateData<T>)
beforeDeleteBefore .delete()async beforeDelete()
afterDeleteAfter .delete()async afterDelete(result: WriteResult, originalId: string)
afterLoadAfter loading via findById or converterasync afterLoad(snapshot: DocumentSnapshot)

Example: Auditing Fields

import { BaseModel, Collection, StringField, TimestampField } from 'fireodm'
import { Timestamp } from 'firebase-admin/firestore'

@Collection('posts')
class Post extends BaseModel {
@StringField({ required: true })
title!: string

@TimestampField({ defaultNow: true })
createdAt?: Timestamp

@TimestampField({ defaultNow: true })
updatedAt?: Timestamp

async beforeSave() {
// Ensure createdAt for new docs
if (!this.createdAt) this.createdAt = Timestamp.now()
// Always update updatedAt
this.updatedAt = Timestamp.now()
}

async afterLoad() {
console.log(`Post ${this.id} loaded at ${new Date().toISOString()}`)
}
}

Notes

  • Error handling: Throwing an error in a before hook will abort the operation.
  • Transactions & Batches: Only before hooks run when queuing operations in transactions or batches. after hooks do not run automatically.

Next: Advanced Usage — explore custom converters, pagination, and Firestore-specific features.