{"post":{"slug":"what-is-a-database-trigger-2026","title":"What is a database trigger? The 2026 guide to when to use one","excerpt":"A trigger is code the database runs automatically when a row changes. Here is how triggers fire, what they are good for, and why they earn suspicion.","category":"code","date":"July 17, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">T</span>riggers are code the database runs by itself, automatically, whenever a specific event happens to a table — a row gets inserted, updated, or deleted. Nothing in the application has to call a trigger; that is the entire point of one. You define it once against the table, and from then on it fires no matter which application, script, or human at a SQL console causes the triggering change. That property makes triggers powerful for enforcing rules no application can accidentally skip, and dangerous for hiding logic nobody remembers is there.\n\n## What changed in 2026\n\n- **Postgres event triggers matured for schema-level auditing** — beyond row-level triggers, teams increasingly log DDL changes (new tables, altered columns) automatically for compliance trails.\n- **ORMs added louder warnings about triggers.** Prisma and Drizzle documentation now explicitly flag that a trigger-modified row can differ from what the ORM thinks it just wrote, prompting more teams to reload the row after a write that has triggers attached.\n- **Change Data Capture (CDC) tools took over use cases that used to need triggers.** Debezium and similar tools now read the write-ahead log directly for replication and syncing, which is more efficient than a trigger-based audit table for high-volume changes.\n\n## How a trigger fires\n\nA trigger is defined against a table and an event, with timing relative to that event:\n\n```sql\nCREATE OR REPLACE FUNCTION set_updated_at()\nRETURNS TRIGGER AS $$\nBEGIN\n  NEW.updated_at = now();\n  RETURN NEW;\nEND;\n$$ LANGUAGE plpgsql;\n\nCREATE TRIGGER trg_orders_updated_at\nBEFORE UPDATE ON orders\nFOR EACH ROW\nEXECUTE FUNCTION set_updated_at();\n```\n\nEvery `UPDATE` on `orders` now stamps `updated_at` automatically, with zero chance an application forgets to set it, because the application is not the one setting it.\n\n## Trigger types\n\n| Timing | Can modify the row? | Can block the operation? | Typical use |\n| --- | --- | --- | --- |\n| BEFORE | Yes | Yes (via exception or NULL return) | Validation, auto-filled columns |\n| AFTER | No (row is already committed to the statement) | No, only by raising an exception | Audit logging, cascading updates |\n| INSTEAD OF | Replaces the operation entirely | Yes, by definition | Making a view writable |\n\nRow-level triggers (`FOR EACH ROW`) fire once per affected row — an `UPDATE` touching 10,000 rows fires the trigger 10,000 times. Statement-level triggers fire exactly once regardless of how many rows the statement touched, which matters enormously for performance on bulk operations.\n\n## What triggers are good for\n\nTriggers earn their place for invariants that must hold no matter what wrote the data: keeping an `updated_at` column honest, maintaining a denormalized count in sync with its source rows, enforcing a business rule that a plain `CHECK` constraint cannot express, or writing an audit-log row for every change to a sensitive table. The common thread is that these are database-level guarantees — they should hold whether the write came from your API, a one-off script, or a teammate at `psql`. If the rule only needs to apply from your own application's code path, a plain function called by that code, or a [stored procedure](/blog/what-is-a-stored-procedure-2026), is usually easier to find and debug later.\n\n## Why triggers get a bad reputation\n\nThe complaint is almost never that triggers do not work; it is that they are invisible. An `UPDATE` statement that looks simple in application code can silently cascade into several more writes, and a developer reading the app's code has no way to know that without also checking the schema. Debugging \"why did this row change when I did not touch it\" is a uniquely frustrating exercise when the answer is a trigger three files away from anywhere your search would look. Triggers that call other triggers compound this further, and a trigger that fails partway through can leave a [database transaction](/blog/what-is-a-database-transaction-2026) rolled back for a reason that appears nowhere in the application's own logs.\n\n## FAQ\n\n**Do triggers run inside the same transaction as the statement that fired them?**\nYes. If a trigger raises an exception, the whole transaction — the original statement and anything already done inside it — rolls back together.\n\n**Are triggers slower than doing the same logic in application code?**\nFor row-level triggers on large bulk operations, yes, noticeably — the per-row overhead adds up. For single-row operations the difference is usually negligible.\n\n**Can a trigger call a stored procedure?**\nYes, and in Postgres a trigger function is itself effectively a small stored procedure written specifically to be called by the trigger mechanism.\n\n**What is the difference between a trigger and a foreign key constraint?**\nA foreign key constraint enforces one specific, well-understood rule (referential integrity) using engine-optimized internals. A trigger can enforce arbitrary custom logic, but is not specialized or optimized the way a constraint is.\n\n## Where to go next\n\n- [What is a stored procedure in 2026](/blog/what-is-a-stored-procedure-2026)\n- [What is a seed file in a database in 2026](/blog/what-is-a-seed-file-database-2026)\n- [What is a database transaction in 2026](/blog/what-is-a-database-transaction-2026)","hue":310,"metaTitle":"What Is a Database Trigger? 2026 Guide","metaDescription":"What is a database trigger in 2026? How BEFORE, AFTER, and INSTEAD OF triggers fire, row-level versus statement-level, and when to actually use one.","keywords":["what is a database trigger 2026","database trigger explained","before vs after trigger","row level vs statement level trigger","postgres trigger example","when to use a database trigger","trigger vs stored procedure","instead of trigger sql","sql trigger best practices"],"tldr":["**A trigger runs automatically on INSERT, UPDATE, or DELETE** — no application code has to call it, which is exactly its appeal and its risk.","**BEFORE triggers can change or block the operation; AFTER triggers react to it** — picking the wrong one is a common source of surprising bugs.","**Row-level triggers fire once per row; statement-level triggers fire once per statement** — a bulk update behaves very differently under each.","**Triggers are best for invariants the database itself must enforce**, not for business logic that belongs in the application layer.","**Skip:** chaining triggers that call other triggers — cascades of hidden logic are one of the hardest things to debug in a codebase."],"readTime":"4 min read","wordCount":834},"related":[{"slug":"what-is-a-stored-procedure-2026","title":"What is a stored procedure? The 2026 guide with tradeoffs","excerpt":"A stored procedure is a named, reusable block of logic that lives inside the database itself. Here is what that buys you in 2026, and what it costs.","category":"code","date":"July 20, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":150},{"slug":"what-is-a-database-transaction-2026","title":"What is a database transaction? The 2026 plain-English guide","excerpt":"A transaction groups several database operations into one unit that either all happen or none do. Here is the lifecycle, not the isolation-level deep dive.","category":"code","date":"July 17, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":290},{"slug":"what-is-a-database-deadlock-2026","title":"What is a database deadlock? The 2026 guide to detection","excerpt":"A database deadlock is not the same as a slow query or a stuck lock wait. Here is how the engine actually detects one, picks a victim, and resolves it.","category":"code","date":"July 17, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":260}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"what-is-a-database-transaction-2026","title":"What is a database transaction? The 2026 plain-English guide","excerpt":"A transaction groups several database operations into one unit that either all happen or none do. Here is the lifecycle, not the isolation-level deep dive.","category":"code","date":"July 17, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":290},"older":{"slug":"what-is-a-debt-consolidation-loan-2026","title":"What is a debt consolidation loan in 2026? When it actually helps","excerpt":"A debt consolidation loan rolls multiple debts into one payment, which can lower interest and simplify life, or quietly extend how long you owe.","category":"finance","date":"July 17, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":305}}