YeetCode
Convex App

Database Triggers

Automatic side effects on database changes using convex-helpers triggers.

YeetCode uses the convex-helpers trigger system to run side effects when database records change. This lets you react to inserts, updates, and deletes without scattering event-handling logic across every mutation.

How It Works

Instead of using Convex's built-in mutation and internalMutation directly, we wrap them with trigger-aware versions that intercept database operations and fire registered callbacks.

Setup

The trigger system is configured in apps/convex/triggers.ts:

import { mutation as rawMutation, internalMutation as rawInternalMutation } from "./_generated/server";
import { Triggers } from "convex-helpers/server/triggers";
import { customCtx, customMutation } from "convex-helpers/server/customFunctions";
import type { DataModel } from "./_generated/dataModel";

const triggers = new Triggers<DataModel>();

// Register triggers for specific tables
triggers.register("users", async (ctx, change) => {
  if (change.operation === "insert") {
    console.log("user created in db event triggered.");
  }
});

// Export wrapped mutation functions
export const mutation = customMutation(rawMutation, customCtx(triggers.wrapDB));
export const internalMutation = customMutation(rawInternalMutation, customCtx(triggers.wrapDB));

Using Trigger-Aware Mutations

Any mutation that should fire triggers must import mutation or internalMutation from triggers.ts instead of from _generated/server:

// ✅ Correct — triggers will fire
import { internalMutation } from "./triggers";

// ❌ Wrong — bypasses triggers
import { internalMutation } from "./_generated/server";

Trigger Callback

The callback receives:

  • ctx — The Convex mutation context
  • change — An object describing the operation (insert, update, or delete) and the affected document
triggers.register("users", async (ctx, change) => {
  if (change.operation === "insert") {
    // New user created — send welcome email, log analytics, etc.
  }
  if (change.operation === "delete") {
    // User deleted — clean up related data
  }
});

Current Triggers

TableOperationBehavior
usersinsertLogs user creation (placeholder for future logic like welcome emails)

When to Use Triggers vs. Inline Logic

  • Triggers are good for cross-cutting concerns (audit logging, analytics, cascading deletes) that should happen regardless of which mutation caused the change.
  • Inline logic is better for business logic specific to a single mutation path.

On this page