← Use cases

Replace polling with live updates

Stop asking your API every few seconds: send each change once, cut requests and server load, and show updates the moment they happen.

Many apps learn about changes by asking: every open page calls your API every few seconds to see if an order, a ticket or a report has changed. Almost every answer is "nothing new", yet each one costs a request, a database query and battery on the user's phone, and a change still waits up to one interval before anyone sees it. With Hibira your server sends each change once, when it happens, and every page that shows that data updates within milliseconds.

What your users get

  • Changes on screen the moment they happen, not up to five seconds later.
  • Pages that stay current in every tab without a reload.
  • Phones that stop waking the radio every few seconds for nothing.

What polling costs

1,000 people with a page open, polling every 5 seconds, make 720,000 requests an hour. If the data changes 200 times in that hour, 99.97% of those requests return nothing new. With push, the same hour is 200 messages, and your API only serves the first load of each page.

Built with

  • Private channels (private-order-42), one per thing a page shows, authorized by your server so each user only hears about their own data.
  • trigger in the official server SDKs, called right after you save a change.
  • pusher:subscription_succeeded, which fires on the first connection and again after every reconnect: the moment to load the current state.

Polling

Every open pageevery 5 seconds
GET /api/orders/42
Your APImostly "nothing new"

Push

Your APIsaves a change
trigger() once
Hibira
WebSocket
Every page showing itupdates at once
Polling asks again and again; push sends each change once

Step 1: find the polling loop

It usually looks like this:

setInterval(async () => {
  render(await fetch('/api/orders/42').then((r) => r.json()));
}, 5000);

Step 2: load once, then listen

Connect as in the JavaScript guide, with a channel authorization endpoint for private channels. Load the current state when the subscription is ready, then apply each change as it arrives:

const load = async () => render(await fetch('/api/orders/42').then((r) => r.json()));

const channel = pusher.subscribe('private-order-42');
channel.bind('pusher:subscription_succeeded', load);
channel.bind('updated', (order) => render(order));

Step 3: send the change when you save it

Wherever the data changes (an API route, a worker, an admin action, a webhook), publish after the write succeeds:

app.patch('/api/orders/:id', requireLogin, async (req, res) => {
  const order = await db.orders.update(req.params.id, req.body);
  await pusher.trigger(`private-order-${order.id}`, 'updated', order);
  res.json(order);
});

Step 4: keep a light safety net

Because the subscription fires again after a reconnect, load() already fills any gap from a dropped connection. Browsers can also freeze background tabs, so load once more when the tab comes back:

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'visible') load();
});

Tips

  • Send what the page needs to redraw, or only an ID and a version for large records, and let the page fetch the rest.
  • Many changes per second to the same data? Batch them, as in live dashboards.
  • Lists that change often can use one channel per list (private-orders-team-7) instead of one per row.
  • Event size depends on your plan: see pricing.
  • Want to see how many people are watching before you publish? See online user counts.