Joey Hershkop

← Back to all projects

Realtime Event Pipeline: Bridging Webhooks to Reactive UI

Production Systems • Ably Pub/Sub, TanStack React Query, TypeScript, PHP, Webhooks

< 100ms
Server-to-Client Signal
Zero
Polling Database Overhead
React Query
Automated Invalidation
Live
Production Feature

The Problem

In modern web applications, critical business state frequently mutates out-of-band on the server. For example, when an external Stripe webhook arrives confirming an invoice payment, or when a partner shares an updated compliance document, the change happens asynchronously in a background worker or webhook handler.

Forcing users to manually refresh their browser or running aggressive background polling intervals creates a sluggish user experience and wastes server resources. The application needed a lightweight, scalable mechanism to reflect backend state changes across active browser sessions instantaneously.

What I Built

I engineered an event-driven pub/sub synchronization bridge connecting backend webhook handlers to frontend React Query state:

  • Transactional Server Publishing: Configured backend controllers to publish lightweight event signals to Ably pub/sub channels strictly after database transaction commits succeed.
  • React Realtime Bridge: Built a custom React context provider (RealtimeProvider) and lifecycle hook (useSubscriptionChannel) that manages persistent WebSocket connections per authenticated company session.
  • Targeted Cache Invalidation: Connected incoming channel events to TanStack React Query key invalidators, prompting the client to re-fetch canonical data without passing bloated or unverified payloads across the socket.
  • Document Sharing Integration: Reused the channel topology to deliver live document list refreshes when collaborative files are shared between organization relationships.

The Interesting Engineering

1. Invalidation Signals vs. Full State Broadcasting

A common pitfall in realtime architectures is attempting to broadcast complete entity trees over WebSockets. This introduces severe synchronization bugs: out-of-order socket packets can overwrite newer local data with stale snapshots, and sensitive fields risk leaking across channel boundaries.

Instead, I implemented an invalidation-first pattern. The pub/sub message carries only minimal metadata (e.g., event type and company identifier). Upon receipt, the React client marks the corresponding query key (e.g., ['subscription', 'company', companyId]) as stale, triggering an authenticated REST fetch that passes through standard permissions and relational validations.

2. Transaction Ordering & Consistency

Realtime notifications must never fire before database mutations commit. If a WebSocket message reaches a fast client before the backend transaction completes, the client's subsequent fetch might retrieve pre-mutation state, resulting in a false "flash" of outdated data.

I structured backend handlers so pub/sub publishing occurs exclusively after database commits succeed, ensuring absolute consistency across read replicas and cache stores.

3. Connection Lifecycle Management

The client-side provider manages token rotation, graceful reconnects, and background tab transitions. When a user sleeps or switches browser tabs, the client cleanly pauses and re-synchronizes upon focus without flooding the backend with duplicate connection handshakes.

Result: Shipped and running in production with real clients, providing seamless UI reactivity for subscription changes and shared document workflows with zero continuous polling overhead.