Asynchronous Data Updates in SPAs Using SSE and Mercure
Frontend applications require constant data updates to reflect backend changes. Standard API calls capture the state at the moment of response, but the backend continues to process events: account top-ups, content moderation, support replies. To address this, push mechanisms based on SSE are used, integrated with the existing OpenAPI specification.
The system combines a pull approach (periodic requests) with push (real-time notifications). SSE provides a unidirectional event stream over HTTP/2, minimizing load compared to polling.
Use Cases for Asynchronous Notifications
Asynchronous notifications are critical in business processes:
- Balance top-up: Displaying the current state for immediate purchases.
- Content moderation: Updating status after review.
- Support replies: Instant notification about a new ticket.
In a typical case, the user—an advertiser or contractor—tracks request counters: the number requiring action, unread comments, placement checks. This data is available via the API endpoint /rest/User/info (operationId: getInfo).
/rest/User/info:
get:
tags:
- User
summary: Returns information about the current user
operationId: getInfo
responses:
200:
description: Information about the current user
content:
application/json:
schema:
type: object
properties:
id:
type: integer
format: int32
minimum: 0
locale:
type: string
enum: [ru, en]
login:
type: string
minimum: 3
maximum: 320
seoCounters:
$ref: '#/components/schemas/SeoCounters'
The SeoCounters schema defines the structure:
SeoCounters:
type: object
properties:
nofRequests:
type: integer
format: int32
minimum: 0
nofLinksStatusNeedApprove:
type: integer
format: int32
minimum: 0
nofLinksWithUnreadComments:
type: integer
format: int32
minimum: 0
nofChanges:
type: integer
format: int32
minimum: 0
nofPlacementCheck:
type: integer
format: int32
minimum: 0
Integrating CDC and Data Platform
Changes in the user_counters table are tracked via CDC (Change Data Capture). The modification stream is sent to the Data Platform—a corporate ETL system. From there, events are pushed to the UI via an SSE hub.
The architecture uses a hexagonal model: the OpenAPI specification automatically generates REST adapters and TypeScript types. For the frontend, the types look like this:
/* Generated by orval */
export interface SeoCounters {
nofRequests?: number;
nofLinksStatusNeedApprove?: number;
nofLinksWithUnreadComments?: number;
nofChanges?: number;
nofPlacementCheck?: number;
}
export type GetInfo200 = {
id: number;
locale: 'ru' | 'en';
login: string;
seoCounters?: SeoCounters;
};
These types are reused for processing SSE events, ensuring type safety.
Implementing SSE in the Application
SSE (Server-Sent Events) is a standard for server pushes, supported by browsers. EventSource connects to the endpoint:
const evtSource = new EventSource('/sse-endpoint', {
withCredentials: true,
});
evtSource.onmessage = (event) => {
const data = JSON.parse(event.data);
// Update the store with typed data
updateSeoCounters(data.seoCounters);
};
For authorization, topics, and scaling, Mercure is used—an open-source hub based on SSE. The hub accepts publications via REST with JWT and broadcasts to subscribers.
Example publication:
curl -d 'topic=https://example.com/books/1' \
-d 'data={"foo": "updated value"}' \
-H 'Authorization: Bearer <JWT>' \
-X POST https://hub/.well-known/mercure
JWT defines publish/subscribe rights for topics. Mercure is deployed as a container with an external database, integrating with CDC streams.
Advantages of the Approach
- Schema Reuse: OpenAPI structures are used in pull and push without duplication.
- Real-Time: Changes are reflected instantly without polling.
- Scalability: The Mercure hub distributes the load.
List of key counters for monitoring:
nofRequests— action requests.nofLinksStatusNeedApprove— links awaiting approval.nofLinksWithUnreadComments— unread comments.nofPlacementCheck— placement checks.
Key Takeaways
- SSE with Mercure provides typed push notifications without WebSocket overhead.
- CDC captures database changes in real-time for ETL processes.
- Auto-generating TypeScript types from OpenAPI ensures data consistency.
- The approach scales for high-load systems with thousands of users.
- JWT in Mercure manages access to notification topics.
— Editorial Team
No comments yet.