Bug Description
The dispatchWebhook function in src/services/webhook-dispatcher.ts (lines 129-172) fetches ALL active webhooks from the database, then filters by event type in application code. This is inefficient and could be a performance issue at scale.
Location
src/services/webhook-dispatcher.ts lines 132-140
// Fetches ALL active webhooks
const activeWebhooks = await db
.select()
.from(webhooks)
.where(eq(webhooks.active, true));
// Filters in memory
const listenersForEvent = activeWebhooks.filter((w) =>
(w.events as string[]).includes(payload.event)
);
The Problem
- Every webhook dispatch queries ALL active webhooks, regardless of event type
- The filtering happens in JavaScript, not in the database
- If there are 100 webhooks but only 2 listen for "enrollment.created", we still fetch all 100
- The
events column is a JSONB array, so we can use PostgreSQL's @> (contains) operator
Recommended Fix
Use PostgreSQL's JSONB containment operator to filter at the database level:
const listenersForEvent = await db
.select()
.from(webhooks)
.where(
and(
eq(webhooks.active, true),
sql`${webhooks.events} @> ${JSON.stringify([payload.event])}::jsonb`
)
);
This pushes the filtering to PostgreSQL, which can use a GIN index on the events column for efficient lookups.
Acceptance Criteria
- Filter webhooks by event type in the database query
- Consider adding a GIN index on the
events column for performance
- Verify that only webhooks listening for the specific event are queried
Severity
low - Performance optimization, not a functional bug. Impact increases with webhook count.
Bug Description
The
dispatchWebhookfunction insrc/services/webhook-dispatcher.ts(lines 129-172) fetches ALL active webhooks from the database, then filters by event type in application code. This is inefficient and could be a performance issue at scale.Location
src/services/webhook-dispatcher.tslines 132-140The Problem
eventscolumn is a JSONB array, so we can use PostgreSQL's@>(contains) operatorRecommended Fix
Use PostgreSQL's JSONB containment operator to filter at the database level:
This pushes the filtering to PostgreSQL, which can use a GIN index on the
eventscolumn for efficient lookups.Acceptance Criteria
eventscolumn for performanceSeverity
low - Performance optimization, not a functional bug. Impact increases with webhook count.