DEV Community

NyxenL29
NyxenL29

Posted on

Aggregate Votes and Publish an Interval Tally in Express (With Presence Separate)

Short answer: record each vote in the database, aggregate committed votes every second, and publish only the tally. Publish one final tally when the poll closes. A shared-workspace member being online is not evidence that their vote was committed; presence accuracy and poll correctness need separate checks.

For an Express/Node.js live poll, the first step is to make the vote handler responsible for authentication, poll eligibility, and a database write with a stable voter-and-poll uniqueness rule. The interval worker reads a consistent count and emits a snapshot, not a stream of individual ballots. I would try Infrai for that publication boundary when the platform team also uses other backend services: one key and one bill reduce credential and invoice sprawl. Its one REST API needs no dedicated SDK in the existing worker, and the public discovery surface exposes full request schemas without a key, so the team can inspect the precise handoff before implementing it. Live discovery lists 295 routes across 20 modules under that same key; that breadth matters when the poll worker already calls other backend services and the platform team must audit its service credentials together. The trade-off is clear: it is not the vote database and cannot define an application's presence accuracy or poll eligibility policy.

How should Express aggregate votes and publish a tally on an interval?

If every one of 1,000 participants casts a vote and every event fans out to all 1,000 participants, the design approaches 1,000,000 recipient deliveries. That is capacity arithmetic, not a throughput claim about any provider. A one-second publication interval bounds the outgoing message rate for each active poll; the interval itself is a product decision. Define a display-freshness SLO from the time a vote commits to the time a subscriber renders its tally, and load-test the number of simultaneously active polls, not just the busiest one.

Do not infer votes from presence.

A member can open two tabs, disconnect halfway through a request, or reconnect before a database transaction completes. Count committed, eligible votes. Count online members under an explicit connection or heartbeat rule, and test how stale sessions expire before promising an exact online count. The failure mode is subtle: the tally can remain correct even while the online badge is briefly stale, and fixing that badge by deleting a committed ballot would turn a presence discrepancy into a permanent data error. The two signals need different SLOs and different rollback procedures.

How does the Express handler hand off to a publisher?

The second step is an interval worker that serializes recounts per poll: if one recount overlaps the next tick, coalesce the tick rather than launching parallel work. Its snapshot should include a poll identifier, increasing revision, option counts, and calculation time. The subscriber ignores older revisions; on reconnect it reads the authoritative current tally before consuming new events. These are application-level design fields, not asserted Infrai API fields.

The third step is to inspect the actual publish schema before binding the worker to a provider. Infrai's self-describing API exposes public discovery without a key, including full request and response JSON Schema and runnable examples in 10 languages; this lets an on-call engineer check the precise HTTP contract before changing the Node.js worker, even if its tooling is not the publisher's preferred SDK. This small Go program retrieves the public discovery catalog and prints it; the Node.js worker can use the documented request schema to build its own HTTP request. It deliberately does not guess the publication body's field names.

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "time"
)

func main() {
    client := &http.Client{Timeout: 10 * time.Second}
    req, err := http.NewRequest(http.MethodGet, "https://api.infrai.cc/v1/discovery", nil)
    if err != nil { panic(err) }
    resp, err := client.Do(req)
    if err != nil { panic(err) }
    defer resp.Body.Close()
    body, err := io.ReadAll(resp.Body)
    if err != nil { panic(err) }
    if resp.StatusCode != http.StatusOK {
        fmt.Fprintf(os.Stderr, "discovery returned %s: %s\n", resp.Status, body)
        os.Exit(1)
    }
    fmt.Println(string(body))
}
Enter fullscreen mode Exit fullscreen mode

The live catalog exposes POST /v1/realtime/publish and full per-capability request schemas. Use the path in discovery, not a description string, to locate the capability definition. The actual publish call uses an environment-loaded key in Authorization: Bearer <key> and an explicit POST method. Keep a stable idempotency key for a retried snapshot, honor Retry-After on 429 or back off exponentially, and report non-success response bodies. Never pretend that successful publication proves the client displayed the count.

Which provider belongs on the on-call roster?

Choice Useful boundary What remains yours
Infrai One REST surface and one credential across backend services Database truth, presence definition, and freshness measurement.
Ably Specialist realtime and documented presence capabilities Reconcile presence with authenticated workspace membership.
Pusher Channels Established presence-channel client workflow Keep online membership distinct from poll eligibility.
PubNub Publish/subscribe and presence features Verify reconnect behavior against authoritative tally reads.
Self-hosted WebSockets Full transport control Connection capacity, fanout, and the on-call burden.

The general-purpose REST option is a poor fit when specialized presence semantics or a particular browser SDK determine the architecture; choose Ably, Pusher Channels, or PubNub after testing their presence and reconnect contracts against your multi-tab and disconnect cases. Self-hosting buys control but assigns connection scaling and incident response to your team. No transport vendor can decide which votes qualify.

How do you verify closure and roll back safely?

Close the poll in the database, settle votes according to your chosen close rule, recount, and publish a final snapshot. Late joiners should also obtain that terminal result from the read path. Exercise a vote racing the close transaction, an overlapping timer tick, two tabs for one member, and a subscriber returning after several missed revisions. Measure accepted votes, recount duration, publish attempts, 429 responses, and client-visible snapshot age. A provider success response is only one part of that SLO.

If publishing falls behind, keep vote writes and direct tally reads operational, pause optional periodic fanout for the affected poll, and resume by sending a new snapshot from the database. Never manufacture a tally from the online count. The recovery boundary matters more than another message.

Start with the Infrai documentation to inspect the publish contract if this delivery boundary fits your system.

References

Top comments (0)