
Why I Used Prisma Client Extensions for CRUD Notifications (MSIS Perspective)
Direct answer (40 words): Prisma extensions hook all models for mutations in one place so audit-relevant events are emitted consistently; try/catch ensures availability of core business writes even if notification delivery fails—matching minimum security integrity expectations for procurement systems.
The problem
Sprinkling afterCreate logic across dozens of server actions creates inconsistent audit trails and missed events when a new model ships. Centralizing on the ORM client keeps one pipeline for “something changed in the domain.”
Implementation from this repo
Extension: mutate first, notify second
export const prisma = prismaBase.$extends({
query: {
$allModels: {
async create({ model, args, query }) {
const result = await query(args);
try {
await emitCrudNotifications(prismaBase, { model, operation: "create", args, result });
} catch {
// Notification writes must never break core mutation flow.
}
return result;
},
The same pattern applies to update, updateMany, upsert, delete, and deleteMany—so deletes and bulk operations stay observable.
Notification routing maps models to permission paths
const MODEL_MODULE_PATHS: Record<string, string[]> = {
Requisition: ["internalProcurement.requisitions"],
PurchaseOrder: ["internalProcurement.purchaseOrders", "externalProcurement.purchaseOrders"],
Invoice: ["internalProcurement.invoices", "externalProcurement.invoices"],
Contract: ["internalProcurement.contracts", "externalProcurement.contracts"],
Shipment: ["internalProcurement.shipments", "externalProcurement.shipments"],
RFQ: ["externalProcurement.rfqs"],
SupplierProfile: ["supplierManagement.suppliers"],
SupplierCategory: ["supplierManagement.categories"],
SupplierPerformanceRecord: ["supplierManagement.performance"],
SupplierDocument: ["supplierManagement.sustainability", "documents"],
SupplierDocumentCategory: ["supplierManagement.categories", "documents"],
Budget: ["financials.budgets"],
User: ["administration.users"],
Department: ["administration.departments"],
Currency: ["administration.currencies"],
Role: ["administration.roles"],
Record: ["records"],
RecordCategoryModel: ["records"],
Archive: ["archives"],
};
Recipients are resolved by users whose JSON permissions include read on the relevant module paths—aligning notifications with RBAC rather than broadcasting to everyone.
MSIS-aligned takeaway
- Integrity: Core procurement writes commit even if notification persistence fails.
- Traceability: Every supported mutation path triggers the same notification pipeline.
- Least privilege: Recipients are filtered by read permission on the affected module.
Read the full case study: CPMS — Comprehensive Procurement Management System