Live dashboards and metrics
Charts and counters that update themselves, with the latest value shown the moment the page opens, and no polling from every open dashboard.
Admin panels, sales dashboards, monitoring screens and stock or crypto tickers are only useful when the numbers are current. Refreshing them every minute means people act on old data, and polling an API from every open dashboard costs you far more than pushing one update to all of them.
What your users get
- Charts and counters that move by themselves as orders, sign-ups or readings come in.
- The latest value the moment the page opens, not a blank tile until the next update.
- A wall screen that stays live all day without a reload.
Built with
- Cache channels (
cache-,private-cache-), which keep the last event for 30 minutes and hand it to every new subscriber right away. - The batch API (
triggerBatch), to send many metrics in one request. - Free fan-out: one published update counts as one message whether ten people or ten thousand are watching.
- The
cache_misswebhook, which tells your server when a new viewer found nothing cached.
Step 1: publish metrics from your backend
Send an update when something changes, or on a short timer for fast-moving numbers. Batching several channels into one request keeps it to one HTTP call:
setInterval(async () => {
const stats = await metrics.snapshot();
await pusher.triggerBatch([
{ channel: 'private-cache-dash-sales', name: 'update', data: { revenue: stats.revenue, orders: stats.orders } },
{ channel: 'private-cache-dash-signups', name: 'update', data: { today: stats.signups } },
]);
}, 5000);Each channel in a batch counts as one message, so a 5-second interval on two tiles is about 35,000 messages a day. Send only when a number has changed to use fewer.
Step 2: subscribe in the dashboard
const sales = pusher.subscribe('private-cache-dash-sales');
sales.bind('update', ({ revenue, orders }) => {
revenueTile.set(revenue);
ordersChart.push(orders);
});
// Nothing published in the last 30 minutes: load the value from your API once
sales.bind('pusher:cache_miss', async () => {
revenueTile.set(await fetch('/api/stats/revenue').then((r) => r.json()));
});The private- prefix means your authorization route decides who may see each dashboard, for example only staff, or only the customer whose account it shows.
Step 3: fill the cache when someone arrives
Subscribe a webhook endpoint to cache_miss, and publish the current value when a viewer opens a dashboard with nothing cached. Every viewer after them gets it from the cache:
for (const e of webhook.getEvents()) {
if (e.name === 'cache_miss' && e.channel === 'private-cache-dash-sales') {
const s = await metrics.snapshot();
await pusher.trigger(e.channel, 'update', { revenue: s.revenue, orders: s.orders });
}
}Tips
- Use one channel per tile or per customer, not one channel for everything: viewers then receive only what they look at.
- Send numbers, not rendered HTML, and keep each update under 10 KB (4 KB on the Free plan).
- Customer-facing dashboards (an analytics page in your SaaS) work the same way with a channel per account:
private-cache-account-123-usage. - Watch your own message and connection counts live on the app page in the portal while you tune the interval.