Promise-based Node.js ORM tool for Postgres, MySQL, MariaDB, SQLite, Microsoft SQL Server, Amazon Redshift and Snowflake's Data Cloud with solid transaction support, relations, eager and lazy loading, read replication and more
—
Lifecycle event system for extending model and query behavior with customizable hooks that execute at specific points during database operations.
Hooks that execute during individual model instance operations.
/**
* Validation hooks
*/
interface ValidationHooks {
/** Before instance validation */
beforeValidate: (instance: Model, options: ValidationOptions) => Promise<void> | void;
/** After successful validation */
afterValidate: (instance: Model, options: ValidationOptions) => Promise<void> | void;
/** When validation fails */
validationFailed: (instance: Model, options: ValidationOptions, error: ValidationError) => Promise<void> | void;
}
/**
* Create/Update/Save hooks
*/
interface CRUDHooks {
/** Before creating new instance */
beforeCreate: (instance: Model, options: CreateOptions) => Promise<void> | void;
/** After creating new instance */
afterCreate: (instance: Model, options: CreateOptions) => Promise<void> | void;
/** Before updating instance */
beforeUpdate: (instance: Model, options: UpdateOptions) => Promise<void> | void;
/** After updating instance */
afterUpdate: (instance: Model, options: UpdateOptions) => Promise<void> | void;
/** Before saving (create or update) */
beforeSave: (instance: Model, options: SaveOptions) => Promise<void> | void;
/** After saving (create or update) */
afterSave: (instance: Model, options: SaveOptions) => Promise<void> | void;
/** Before destroying instance */
beforeDestroy: (instance: Model, options: InstanceDestroyOptions) => Promise<void> | void;
/** After destroying instance */
afterDestroy: (instance: Model, options: InstanceDestroyOptions) => Promise<void> | void;
/** Before restoring soft-deleted instance */
beforeRestore: (instance: Model, options: RestoreOptions) => Promise<void> | void;
/** After restoring soft-deleted instance */
afterRestore: (instance: Model, options: RestoreOptions) => Promise<void> | void;
/** Before upserting */
beforeUpsert: (values: any, options: UpsertOptions) => Promise<void> | void;
/** After upserting */
afterUpsert: (result: [Model, boolean], options: UpsertOptions) => Promise<void> | void;
}Usage Examples:
// Define hooks in model definition
class User extends Model {}
User.init({
firstName: DataTypes.STRING,
lastName: DataTypes.STRING,
email: DataTypes.STRING,
hashedPassword: DataTypes.STRING,
lastLoginAt: DataTypes.DATE
}, {
sequelize,
modelName: 'user',
hooks: {
// Hash password before creating user
beforeCreate: async (user, options) => {
if (user.password) {
user.hashedPassword = await bcrypt.hash(user.password, 10);
delete user.password;
}
},
// Update login timestamp after creation
afterCreate: async (user, options) => {
console.log(`User ${user.email} created at ${new Date()}`);
},
// Hash password before updates too
beforeUpdate: async (user, options) => {
if (user.changed('password')) {
user.hashedPassword = await bcrypt.hash(user.password, 10);
delete user.password;
}
},
// Log validation failures
validationFailed: (user, options, error) => {
console.log(`Validation failed for user: ${error.message}`);
}
}
});
// Add hooks after model definition
User.addHook('beforeSave', async (user, options) => {
user.email = user.email.toLowerCase();
});
User.addHook('afterDestroy', (user, options) => {
console.log(`User ${user.email} was deleted`);
});Hooks that execute during bulk database operations.
/**
* Bulk operation hooks
*/
interface BulkHooks {
/** Before bulk create operation */
beforeBulkCreate: (instances: Model[], options: BulkCreateOptions) => Promise<void> | void;
/** After bulk create operation */
afterBulkCreate: (instances: Model[], options: BulkCreateOptions) => Promise<void> | void;
/** Before bulk update operation */
beforeBulkUpdate: (options: UpdateOptions) => Promise<void> | void;
/** After bulk update operation */
afterBulkUpdate: (options: UpdateOptions) => Promise<void> | void;
/** Before bulk destroy operation */
beforeBulkDestroy: (options: DestroyOptions) => Promise<void> | void;
/** After bulk destroy operation */
afterBulkDestroy: (options: DestroyOptions) => Promise<void> | void;
/** Before bulk restore operation */
beforeBulkRestore: (options: RestoreOptions) => Promise<void> | void;
/** After bulk restore operation */
afterBulkRestore: (options: RestoreOptions) => Promise<void> | void;
/** Before bulk sync operation */
beforeBulkSync: (options: SyncOptions) => Promise<void> | void;
/** After bulk sync operation */
afterBulkSync: (options: SyncOptions) => Promise<void> | void;
}Usage Examples:
// Bulk operation hooks
User.addHook('beforeBulkCreate', (instances, options) => {
console.log(`About to create ${instances.length} users`);
// Modify all instances before creation
instances.forEach(user => {
user.email = user.email.toLowerCase();
user.createdAt = new Date();
});
});
User.addHook('afterBulkUpdate', (options) => {
console.log('Bulk update completed');
// Clear cache after bulk operations
cache.clear('users');
});
User.addHook('beforeBulkDestroy', (options) => {
// Log what's about to be deleted
console.log('About to delete users matching:', options.where);
});Hooks that execute around database query operations.
/**
* Query-related hooks
*/
interface QueryHooks {
/** Before find operations */
beforeFind: (options: FindOptions) => Promise<void> | void;
/** After find operations */
afterFind: (instances: Model | Model[] | null, options: FindOptions) => Promise<void> | void;
/** Before count operations */
beforeCount: (options: CountOptions) => Promise<void> | void;
/** Before find, after include expansion */
beforeFindAfterExpandIncludeAll: (options: FindOptions) => Promise<void> | void;
/** Before find, after options processing */
beforeFindAfterOptions: (options: FindOptions) => Promise<void> | void;
/** Before any SQL query execution */
beforeQuery: (options: QueryOptions, query: string) => Promise<void> | void;
/** After any SQL query execution */
afterQuery: (options: QueryOptions, query: string) => Promise<void> | void;
}Usage Examples:
// Query hooks for caching and logging
User.addHook('beforeFind', (options) => {
console.log('Finding users with options:', options);
// Add default ordering if not specified
if (!options.order) {
options.order = [['createdAt', 'DESC']];
}
});
User.addHook('afterFind', (result, options) => {
if (Array.isArray(result)) {
console.log(`Found ${result.length} users`);
} else if (result) {
console.log(`Found user: ${result.email}`);
} else {
console.log('No user found');
}
});
// Global query logging
sequelize.addHook('beforeQuery', (options, query) => {
console.time('query');
console.log('Executing query:', query);
});
sequelize.addHook('afterQuery', (options, query) => {
console.timeEnd('query');
});Hooks that execute during model definition and association setup.
/**
* Model definition hooks
*/
interface ModelDefinitionHooks {
/** Before model definition */
beforeDefine: (attributes: ModelAttributes, options: DefineOptions) => Promise<void> | void;
/** After model definition */
afterDefine: (model: typeof Model) => Promise<void> | void;
/** Before model initialization */
beforeInit: (attributes: ModelAttributes, options: InitOptions) => Promise<void> | void;
/** After model initialization */
afterInit: (model: typeof Model) => Promise<void> | void;
/** Before association setup */
beforeAssociate: (model: typeof Model, associations: any) => Promise<void> | void;
/** After association setup */
afterAssociate: (model: typeof Model, associations: any) => Promise<void> | void;
}Usage Examples:
// Model definition hooks
sequelize.addHook('beforeDefine', (attributes, options) => {
// Automatically add timestamps if not present
if (!options.timestamps && !attributes.createdAt) {
attributes.createdAt = DataTypes.DATE;
attributes.updatedAt = DataTypes.DATE;
options.timestamps = true;
}
});
sequelize.addHook('afterDefine', (model) => {
console.log(`Model ${model.name} defined with attributes:`, Object.keys(model.getAttributes()));
});Hooks that execute during database connection and synchronization operations.
/**
* Connection and sync hooks
*/
interface ConnectionSyncHooks {
/** Before database connection */
beforeConnect: (config: any) => Promise<void> | void;
/** After database connection */
afterConnect: (connection: any, config: any) => Promise<void> | void;
/** Before connection disconnect */
beforeDisconnect: (connection: any) => Promise<void> | void;
/** After connection disconnect */
afterDisconnect: (connection: any) => Promise<void> | void;
/** Before connection pool acquire */
beforePoolAcquire: (config: any) => Promise<void> | void;
/** After connection pool acquire */
afterPoolAcquire: (connection: any, config: any) => Promise<void> | void;
/** Before database sync */
beforeSync: (options: SyncOptions) => Promise<void> | void;
/** After database sync */
afterSync: (options: SyncOptions) => Promise<void> | void;
}Usage Examples:
// Connection hooks
sequelize.addHook('beforeConnect', (config) => {
console.log('Attempting to connect to database:', config.database);
});
sequelize.addHook('afterConnect', (connection, config) => {
console.log('Successfully connected to database');
});
sequelize.addHook('beforeSync', (options) => {
console.log('Starting database synchronization');
});
sequelize.addHook('afterSync', (options) => {
console.log('Database synchronization completed');
});Methods for managing hooks dynamically.
/**
* Add hook to model or sequelize instance
* @param hookType - Type of hook
* @param name - Hook name (optional)
* @param fn - Hook function
*/
addHook(hookType: string, name: string, fn: Function): void;
addHook(hookType: string, fn: Function): void;
/**
* Remove hook from model or sequelize instance
* @param hookType - Type of hook
* @param name - Hook name
*/
removeHook(hookType: string, name: string): boolean;
/**
* Check if hook exists
* @param hookType - Type of hook
* @param name - Hook name
*/
hasHook(hookType: string, name?: string): boolean;
/**
* Run hooks manually
* @param hookType - Type of hook
* @param args - Arguments to pass to hooks
*/
runHooks(hookType: string, ...args: any[]): Promise<void>;Usage Examples:
// Named hooks can be removed later
User.addHook('beforeCreate', 'hashPassword', async (user, options) => {
user.hashedPassword = await bcrypt.hash(user.password, 10);
});
User.addHook('beforeCreate', 'setDefaults', (user, options) => {
user.isActive = user.isActive !== false;
user.role = user.role || 'user';
});
// Remove specific hook
User.removeHook('beforeCreate', 'hashPassword');
// Check if hook exists
if (User.hasHook('beforeCreate', 'setDefaults')) {
console.log('setDefaults hook is active');
}
// Run hooks manually
await User.runHooks('beforeCreate', userInstance, options);Advanced hook configuration and context access.
/**
* Hook function signature with context
*/
type HookFunction<T = any> = (
this: typeof Model | Model,
...args: any[]
) => Promise<void> | void;
/**
* Hook options
*/
interface HookOptions {
/** Hook priority (higher runs first) */
priority?: number;
/** Hook context */
context?: any;
}Usage Examples:
// Hook with priority (higher priority runs first)
User.addHook('beforeCreate', 'validation', async function(user, options) {
// Validation logic
}, { priority: 100 });
User.addHook('beforeCreate', 'defaults', function(user, options) {
// Set defaults (runs after validation)
}, { priority: 50 });
// Access model context in hook
User.addHook('afterFind', function(result, options) {
// 'this' refers to the User model
console.log(`Query executed on ${this.name} model`);
});
// Instance method with hook context
User.addHook('beforeSave', function(user, options) {
// 'this' refers to the model class
console.log(`Saving ${this.name} instance`);
});Automatic tracking of data changes.
// Audit trail implementation
class AuditLog extends Model {}
AuditLog.init({
tableName: DataTypes.STRING,
recordId: DataTypes.INTEGER,
action: DataTypes.ENUM('CREATE', 'UPDATE', 'DELETE'),
oldValues: DataTypes.JSON,
newValues: DataTypes.JSON,
userId: DataTypes.INTEGER,
timestamp: DataTypes.DATE
}, { sequelize, modelName: 'auditLog' });
// Add audit hooks to User model
User.addHook('afterCreate', async (user, options) => {
await AuditLog.create({
tableName: 'users',
recordId: user.id,
action: 'CREATE',
newValues: user.toJSON(),
userId: options.userId,
timestamp: new Date()
});
});
User.addHook('afterUpdate', async (user, options) => {
await AuditLog.create({
tableName: 'users',
recordId: user.id,
action: 'UPDATE',
oldValues: user._previousDataValues,
newValues: user.dataValues,
userId: options.userId,
timestamp: new Date()
});
});
User.addHook('afterDestroy', async (user, options) => {
await AuditLog.create({
tableName: 'users',
recordId: user.id,
action: 'DELETE',
oldValues: user.toJSON(),
userId: options.userId,
timestamp: new Date()
});
});Automatic cache invalidation.
// Cache management hooks
const cache = new Map();
User.addHook('afterCreate', (user, options) => {
// Invalidate user list cache
cache.delete('users:list');
cache.delete(`users:${user.id}`);
});
User.addHook('afterUpdate', (user, options) => {
// Invalidate specific user cache
cache.delete(`users:${user.id}`);
cache.delete('users:list');
});
User.addHook('afterDestroy', (user, options) => {
// Remove from cache
cache.delete(`users:${user.id}`);
cache.delete('users:list');
});
User.addHook('afterFind', (result, options) => {
// Cache found results
if (Array.isArray(result)) {
cache.set('users:list', result);
} else if (result) {
cache.set(`users:${result.id}`, result);
}
});Data cleaning and validation.
// Comprehensive validation and sanitization
User.addHook('beforeValidate', (user, options) => {
// Trim whitespace
if (user.firstName) user.firstName = user.firstName.trim();
if (user.lastName) user.lastName = user.lastName.trim();
if (user.email) user.email = user.email.trim().toLowerCase();
// Remove extra spaces
if (user.bio) user.bio = user.bio.replace(/\s+/g, ' ').trim();
});
User.addHook('beforeSave', async (user, options) => {
// Encrypt sensitive data
if (user.changed('socialSecurityNumber')) {
user.socialSecurityNumber = await encrypt(user.socialSecurityNumber);
}
// Generate slugs
if (user.changed('firstName') || user.changed('lastName')) {
user.slug = slugify(`${user.firstName}-${user.lastName}`);
}
});Event-driven notifications.
// Notification system
User.addHook('afterCreate', async (user, options) => {
// Send welcome email
await emailService.sendWelcomeEmail(user.email, {
firstName: user.firstName
});
// Create notification
await Notification.create({
userId: user.id,
type: 'welcome',
message: 'Welcome to our platform!',
isRead: false
});
});
User.addHook('afterUpdate', async (user, options) => {
// Notify on important changes
if (user.changed('email')) {
await emailService.sendEmailChangeNotification(
user.previous('email'),
user.email
);
}
if (user.changed('password')) {
await emailService.sendPasswordChangeNotification(user.email);
}
});Install with Tessl CLI
npx tessl i tessl/npm-sequelize