Hooks
Client lifecycle hooks for auditing, tracing, metrics, and request-scoped logging.
Client hooks are the side-effect layer around Better operations. They are optional — if you do not need them, do not pass them. They are ideal for cross-cutting concerns you do not want duplicated in every call:
- audit trails
- tracing and metrics
- authorization checks
- request-scoped logging
You register hooks when creating the client:
const client = better(db, {
schema,
hooks: {
beforeCreate(ctx) {
console.log('beforeCreate', ctx.action, ctx.table);
},
afterCreate(ctx) {
console.log('afterCreate', ctx.row);
},
beforeQuery(ctx) {
console.log('beforeQuery', ctx.action, ctx.args.where);
},
afterQuery(ctx) {
console.log('afterQuery', ctx.action, ctx.result);
},
},
});Available hooks
beforeCreate/afterCreatebeforeUpdate/afterUpdatebeforeDelete/afterDeletebeforeQuery/afterQuery
Query hooks cover findMany, findFirst, findOne, findUnique,
count, exists, and paginate. Create/update/delete hooks also fire for
the matching half of upsert.
beforeTransactionafterTransactionCommitafterTransactionRollbackonTransactionError
beforeRawafterRawonRawError
onError— fires when any operation or hook throws, with theaction,args,error, and thestageit failed in.
Request metadata with meta
Every operation accepts a meta object. Use it to thread request-scoped context into hooks:
await client.users.findMany({
where: { active: true },
meta: { requestId: 'req_123', userId: 'admin_7' },
});const client = better(db, {
schema,
hooks: {
beforeQuery(ctx) {
console.log(ctx.meta?.requestId);
},
},
});Transaction lifecycle
const client = better(db, {
schema,
hooks: {
beforeTransaction(ctx) {
console.log('tx start', ctx.name, ctx.depth);
},
afterTransactionCommit(ctx) {
console.log('tx committed', ctx.attempt);
},
afterTransactionRollback(ctx) {
console.log('tx rolled back', ctx.reason);
},
onTransactionError(ctx) {
console.error('tx error', ctx.error);
},
},
});Raw SQL lifecycle
const client = better(db, {
schema,
hooks: {
beforeRaw(ctx) {
console.log(ctx.action, ctx.comment, ctx.name);
},
afterRaw(ctx) {
console.log(ctx.result);
},
onRawError(ctx) {
console.error(ctx.query, ctx.error);
},
},
});Hooks vs plugins
Observe with hooks, change behavior with plugins
Client hooks should observe and coordinate — logging, tracing, metrics.
If you want to mutate an operation (rewrite the where, inject fields, add
delegate methods, expose typed operation args), that is
plugin territory.