fidum/laravel-nova-metrics-polling
Adds configurable polling to Laravel Nova metrics cards, automatically refreshing dashboards at set intervals without manual reloads. A lightweight way to keep metric values current during monitoring, demos, or live operations, with simple setup for Nova users.
fidum/laravel-nova-metrics-polling is a thin add-on for Laravel Nova that makes metric cards re-query on an interval instead of only on page load or manual refresh. It is the difference between a dashboard you look at and a dashboard you leave on a wall monitor.
Install
composer require fidum/laravel-nova-metrics-polling
As of 1.0.5 the package supports Laravel 13 alongside the earlier supported versions, so it is safe on a freshly upgraded app. Note that Nova itself has its own version constraint — check composer.json in the package before upgrading a Nova 3 app; 1.x targets Nova 4+.
Publish the config
php artisan vendor:publish --tag=nova-metrics-polling-config
(Confirm the tag in the package's service provider / README — it is the fastest way to see the exact set of keys your installed version exposes.)
Where to look first
config/nova-metrics-polling.php — the global interval and any enable/disable switches. This is the file you will actually touch 90% of the time.src/ — the trait/concern you apply to your metric classes, plus how the interval is serialised into the card's JSON payload.First use case — an ops dashboard card:
// app/Nova/Metrics/OrdersToday.php
class OrdersToday extends Value
{
// apply the package's polling concern here (see README for the exact trait name)
// and set the per-card interval, e.g. every 15 seconds
}
Register it as usual in NovaServiceProvider::cards(), set the global interval to something sane (10–30s), and open the dashboard. If the number changes without you hitting refresh, you are done. Anything beyond that is tuning.
Global default, per-card override. Set one conservative interval globally (30s is a good default) and shorten it only on the couple of cards that genuinely need to be live — "orders in the last hour", queue depth, on-call status. Value/Trend cards over transactional tables do not need 5-second polling.
Apply it selectively, not to the dashboard. The package is most useful on a dedicated "Ops" / "Live" dashboard in NovaServiceProvider::dashboards(). Keeping the main dashboard static avoids multiplying request volume for users who are just clicking around.
Pair every polled card with a matching cache TTL. Nova metrics default to cacheFor(5 minutes). If you poll every 15 seconds against a 5-minute cache, the card will happily re-fetch the same stale value 20 times. Either shorten the cache to at or below the interval:
public function cacheFor()
{
return now()->addSeconds(15);
}
…or disable caching entirely on cheap queries. This is the single most important integration detail.
Compose with Nova's built-in refresh behaviour. Polling and refreshWhenActionRuns / the manual refresh button solve different problems; polling handles ambient updates, actions handle "I just changed something". Leave both on for action-heavy dashboards — the action refresh is instant, the poll is the safety net.
Scale by hiding, not by tuning. Every polling client is a GET /nova-api/metrics/{metric} request on a fixed cadence. Before you drop the interval, use ->canSee(fn ($request) => $request->user()->isAdmin()) to keep heavy Trend cards off the majority of users' screens and reserve fast polling for the two or three people watching the wall display.
Keep polled queries cheap. A polled metric should be an indexed aggregate or a cached value, not a withCount over a million rows. If the card can be answered from a Redis key updated by a scheduled job or an event listener, do that and let the poll do nothing but read it.
Cache TTL longer than the interval is the #1 confusion. Symptoms: the card "refreshes" (spinner, network request) but the number never moves. Check cacheFor() on the metric before you go digging into the package.
Watch the API throttle. All metric polling goes through Nova's nova-api routes and therefore the api rate limiter. A room full of wallboards, or a dashboard with eight polled cards at 5s, will produce 429s that look like "polling randomly stops". Raise the limiter for the nova-api group or lengthen the interval.
Browser tab throttling. Browsers clamp setInterval-driven work in background tabs (and pause it entirely on mobile). Do not build alerting or "last updated" semantics on top of the poll cadence — treat it as best-effort freshness, and surface the timestamp of the underlying data rather than of the request.
Nova version coupling. 1.0.5 only adds Laravel 13 support; it does not change the Nova requirement. If you are still on Nova 3, you need an older tag, and the config keys will differ from the current README.
Lockfile drift after upgrading. Because the change is just an added framework constraint, composer update fidum/laravel-nova-metrics-polling on a Laravel 13 app is enough — but run composer why-not if the package refuses to move; the blocker is usually a transitive Nova or illuminate/* constraint, not this package.
Timezones and ranges still apply. Polling refreshes the data, not the card's configured range or ->timezone(). A Trend card set to "last 30 days" will re-fetch 30 days every interval — make sure that is what you want, and prefer short ranges for polled trend cards.
Debugging path. Open the network tab and look at the repeated /nova-api/metrics/{uriKey} calls. If they are firing on schedule but the payload is identical, it is caching. If they are firing more than expected, check that the concern has not been applied to a base metric class you extend everywhere. If they are not firing at all, the interval is not making it into the card's serialised payload — dump the metric's jsonSerialize() and confirm the polling key is present.
Extension points. The published config is the supported lever for global behaviour; the per-card interval property (or override of the concern's interval method) is the supported lever for individual metrics. If you need custom behaviour — pausing polling when the window is hidden, a "live" toggle in the UI — subclass the metric and override the serialisation rather than editing vendor files, so composer update stays safe.
How can I help you explore Laravel packages today?