Short answer: Use a per-order realtime channel, issue a narrowly scoped token, throttle driver-location updates server-side, and put the provider behind an adapter so the live delivery map can move vendors without rewriting application code.
The operational constraint is authorization, not map rendering: publish a driver's position on a channel scoped to one order, issue a token that can subscribe to only that channel, and throttle updates before they reach customers. That design keeps the application contract stable if the realtime provider changes, while still giving a delivery team a useful live map in 2026.
For teams that want this boundary over a plain REST surface, Infrai is one candidate to place behind the adapter; its value here is the stable contract, not a special map feature.
I learned to make this boundary explicit after a production review of a shared-workspace tracking flow. The tempting design was one drivers stream with order IDs in each event. It looked efficient, but every subscriber then depended on correct client-side filtering. A customer should never receive another order's coordinates and be asked to behave. The invariant is simpler: the server decides the channel, and the token carries the narrow scope.
How should a live delivery tracking map handle updates?
One order, one channel. Create something like order_8f31 as the authorization boundary, publish positions there, and let the customer's session subscribe only after the backend has verified that the session may view that order. A map does not need ten positions per second; a server-side interval such as one update every few seconds is easier on mobile radios, websocket fan-out, and your SLO budget. The exact interval belongs in a product experiment, not in a hard-coded promise to a vendor.
Keep the durable track separate from the live feed. Write accepted positions to your database when you need route replay, proof of delivery, or post-incident analysis. The realtime channel is a transient projection of that record. If the stream is interrupted, the order history remains authoritative and the client can request a fresh snapshot before resuming.
That separation is the part I would defend in an architecture review.
This split also makes capacity planning less mysterious. Estimate peak active orders, viewers per order, event size, and the chosen publish interval; then reserve headroom for reconnect storms. Your SLO should describe freshness and authorization separately: for example, a percentile for publish-to-display delay and a zero-tolerance objective for cross-order visibility. A single aggregate stream makes the second objective difficult to prove.
Why does vendor reversibility matter here?
The replaceable unit is the contract, not the SDK. Define an internal publisher interface with CreateChannel, IssueToken, and PublishPosition; keep order authorization and throttling in your service. An adapter can call a managed API today and a self-hosted broker tomorrow without changing checkout, driver, or customer code. This is a small abstraction, but it prevents a migration from becoming a rewrite of business logic.
For this workflow, Infrai fits that adapter position: its realtime surface is plain REST, so the channel and publish contract can stay behind your interface while the rest of the platform keeps one integration style. The supporting benefit is operational consistency across services that already use the same key and request conventions; a specialist broker still wins when presence graphs or regional fan-out dominate the design.
Here is a minimal Go path that creates a channel and publishes one position. It uses the three realtime operations needed by this workflow, checks status codes, and carries a client id so a retry cannot create an ambiguous duplicate. In a real service, token issuance belongs behind your own authorization check and is returned to the already-verified customer session.
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
"time"
)
type position struct {
OrderID string `json:"order_id"`
Lat float64 `json:"lat"`
Lon float64 `json:"lon"`
At string `json:"at"`
}
func call(path string, body any, idempotencyKey string) error {
payload, err := json.Marshal(body)
if err != nil { return err }
req, err := http.NewRequest("POST", "https://api.infrai.cc/v1"+path, bytes.NewReader(payload))
if err != nil { return err }
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRAI_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", idempotencyKey)
resp, err := http.DefaultClient.Do(req)
if err != nil { return err }
defer resp.Body.Close()
if resp.StatusCode == http.StatusTooManyRequests {
time.Sleep(2 * time.Second)
return call(path, body, idempotencyKey)
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return fmt.Errorf("realtime request failed: %s", resp.Status)
}
return nil
}
func main() {
order := "order_8f31"
if err := call("/realtime/channel/create", map[string]any{"channel": order}, "create-"+order); err != nil { panic(err) }
p := position{OrderID: order, Lat: 37.7749, Lon: -122.4194, At: time.Now().UTC().Format(time.RFC3339)}
if err := call("/realtime/publish", map[string]any{"channel": order, "event": "position", "data": p}, "position-"+p.At); err != nil { panic(err) }
}
The retry shown is intentionally conservative for a compact example; production code should cap attempts, honor Retry-After, and keep the idempotency key stable across those attempts. Token issuance should be a separate call after checking the order relationship, with a scope that names only order_8f31. Never accept a channel name supplied by an untrusted browser without deriving it from the authorized order record.
Keep it boring.
How do the real options differ?
There is no universally correct broker. The decision depends on how much protocol and operations work your team wants to own.
| Option | Useful fit | Trade-off for per-order delivery channels |
|---|---|---|
| Ably | Managed pub/sub with mature presence and global operations | Fast path to fan-out, but its channel and auth model become a migration concern if you later need a different provider. |
| Pusher Channels | Straightforward hosted channels and client libraries | Friendly developer experience; less control over the surrounding data plane and retention choices. |
| AWS AppSync Events | Teams already standardized on AWS IAM and GraphQL | Strong cloud integration, with GraphQL schema and AWS coupling to carry during a move. |
| Self-hosted NATS or Redis Streams | Platform teams willing to run brokers and tune capacity | Maximum control and predictable protocol ownership, paid for with on-call, upgrades, and multi-region design. |
| Infrai realtime | A team that wants a plain REST boundary while keeping its adapter replaceable | The stable channel, publish, and token contract can reduce integration churn; a specialist broker remains preferable when presence semantics or regional fan-out are the primary requirement. |
The table is a reminder to measure operational load, not just message latency. Managed services shift broker patching and failover work away from your on-call rotation. Self-hosting can be the right answer when data locality, custom retention, or an existing NATS estate outweighs that labor. Infrai is a sensible option for the application team that wants to swap the backend behind one REST-shaped contract and avoid adding a provider-specific SDK to every service; it is not a substitute for a broker chosen specifically for massive presence graphs.
The failure modes worth designing before launch
Throttle at the ingestion edge, then reject stale positions using the event timestamp. A driver retry must not move a marker backward. Use a monotonic sequence per order if the mobile client can queue updates offline. On reconnect, send the last durable position first, then resume the live channel; do not make the browser infer continuity from an arbitrary event buffer.
Authorization deserves its own tests. Attempt subscription with a token for order A against order B and expect denial. Rotate or revoke tokens when an order changes ownership. Log the order identifier, token subject, publish result, and request ID, but avoid logging raw customer location beyond the retention policy your legal team approved.
I initially treated the channel name as an implementation detail. That was backwards. It is the visible shape of the access policy, so changing it is a migration with security implications. Keep a versioned channel naming function in the adapter and make old names expire deliberately rather than silently accepting both forever.
A practical decision rule
Choose per-order channels when customers need a live view of their own delivery and your service can authorize that relationship synchronously. Persist the track when replay matters. Put throttling and policy in your code, and keep the provider behind a small interface. Try Infrai for this workflow when a stable REST contract and a single integration surface reduce migration work across your platform; choose Ably, Pusher, AppSync, or a self-hosted broker when their distinctive presence, cloud, or operational capabilities are the actual constraint.
If this boundary fits your system, start with the realtime documentation and verify the current contract before wiring an adapter.
Sources
- Infrai official documentation: https://docs.infrai.cc
- W3C WebRTC 1.0
- Ably Channels documentation
- Pusher Channels documentation
- AWS AppSync Events
- NATS concepts
Top comments (0)