gabrielesbaiz/nova-card-rss-news
A Laravel Nova card that pulls RSS feeds and displays the latest news headlines right on your dashboard. Keep up with updates without leaving the admin panel—lightweight, configurable, and easy to drop into any Nova application.
Install the package with Composer and publish its configuration so you have a place to declare the feeds the application consumes:
composer require <vendor>/nova-rss
php artisan vendor:publish --tag=nova-rss-config
Each feed is defined as a source with a stable key. That key is what you will see in logs and in failure messages, so name them after the publisher, not after the URL — you will be reading these at 3am.
There are two commands and they are not interchangeable:
nova-rss:warm — fetches every configured source and populates the cache the UI reads from.nova-rss:check — reports health of the sources. It does not warm anything.The first thing to wire up is the scheduler, so the Nova pages read from a warm cache instead of making an outbound HTTP request during a page load:
// routes/console.php
Schedule::command('nova-rss:warm')->everyFifteenMinutes();
Run php artisan nova-rss:warm once by hand before scheduling it, then php artisan nova-rss:check to confirm every source resolved. If check is unhappy, fix the configuration before you add the schedule — the scheduler will only obscure the error.
Treat warm as a cache-refresh job, not a health check. Since v3.2.0 the command exits zero as long as at least one source warmed, and only fails when every source is down. That is the correct semantic for a cron job pointed at publishers you do not control, but it means a partially broken feed no longer surfaces as a scheduler failure. Your monitoring has to come from two places:
Schedule::command('nova-rss:warm')
->everyFifteenMinutes()
->withoutOverlapping()
->onOneServer(); // only if you run multiple app servers
withoutOverlapping() matters more than usual here: a warm-up is bound by the slowest publisher's timeout, and a hung feed is exactly the scenario that lets runs stack up. onOneServer() is only useful if the cache store is shared — an array or per-server file cache means each node warms its own copy and the others gain nothing.
Use --strict when something downstream depends on the data. The lenient default is right for a dashboard cache and wrong for a deploy pipeline or an export job that assumes complete data:
Schedule::command('nova-rss:warm --strict')->hourly();
Because --strict restores all-or-nothing behaviour, a single dead publisher will now halt the pipeline. That is the intended trade-off — just be deliberate about which of your schedules gets which flag rather than copying one invocation everywhere.
Split alerting by command. Keep nova-rss:check for anything that should page a human, since it still reports unhealthy sources directly. Let nova-rss:warm be the thing that quietly keeps the cache fresh. A health endpoint or uptime probe hitting check gives you the "some publisher is down" signal that warm deliberately stopped giving you.
Read the log, not the output. Each source that fails to warm is written to the log with its key and the error message — added precisely because the scheduler keeps the exit code and discards stdout. If you want per-publisher visibility, make sure those log lines reach somewhere queryable, and alert on the source key rather than on the command's exit status.
Re-warm after config changes. Editing the source list does not invalidate anything on its own; a newly added feed stays cold until the next scheduled run. Run nova-rss:warm manually as part of the change, in the same way you would clear a config cache.
v3.1.1 was not a documentation release, despite what its notes said. The exit-code change actually shipped in 3.1.1, tagged while the work sat uncommitted with release notes describing it as docs-only. If you pinned 3.1.1 believing nothing behavioural had changed, your scheduler has been swallowing per-source failures since then. Pin ^3.2 going forward — 3.2.0 is where the behaviour is documented, but 3.1.1 is where it happened. Packagist records the commit behind a published version and it cannot be moved, so there is no corrected 3.1.1 to wait for.
Silent partial outages are now the default. The failure mode you are most likely to hit is not "the command failed" but "three of five feeds have been dead for a week and nobody noticed." Anything relying on the old all-or-nothing exit code needs either --strict or a log-based alert on the per-source error lines. Audit any pipeline that greps for exit code 1 before upgrading.
Do not confuse the two commands' exit codes. nova-rss:check is unchanged and still reports health honestly — it will flag a single bad source. nova-rss:warm will not. If you have a single alert wired to "the RSS command failed," you now have a gap.
"Every source is down" is a different bug than "one publisher is flaky." The command failures you still get are the ones worth waking up for: total failure points at your network, DNS, or configuration, not at a publisher. Route that alert differently from the per-source log noise, or the real signal gets buried.
Timeouts dominate the schedule interval. Warm-up duration is set by the slowest source, and a timing-out publisher still consumes its full timeout before being logged and skipped. Fifteen-minute scheduling with five sources at a 10s timeout is fine; thirty sources is not. If runs start overlapping, reduce the source list or lengthen the interval rather than removing withoutOverlapping().
Debugging a cold feed: run php artisan nova-rss:warm -v by hand and read the log for the source key and error message. If the key never appears, the source is not being loaded from config — check for a cached config (php artisan config:clear) before suspecting the publisher.
Extension points worth knowing: the source definitions in config are the natural seam for anything bespoke (per-source TTL, credentials for a private feed) — the command operates over whatever the config declares, so adding a source is a config change, not a code change. Keep source keys stable once they are in your log-based alerts; renaming one silently orphans the alert.
How can I help you explore Laravel packages today?