Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Spatie Lighthouse Laravel Package

rawand201/filament-spatie-lighthouse

Seamlessly integrate Spatie Lighthouse with Filament. This plugin adds a native admin panel for exploring and managing Lighthouse GraphQL schemas and queries, helping Laravel developers keep GraphQL workflows visible and accessible inside Filament.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Filament plugin wrapping spatie/lighthouse — you get a page in your panel to run audits, plus commands, scheduling, notifications and an optional REST API. You do not need to know the Spatie package to use it, but you do need a host that can run headless Chrome.

Minimal path:

composer require rawand201/filament-spatie-lighthouse
php artisan vendor:publish --tag="filament-spatie-lighthouse-migrations"
php artisan migrate

Then register the plugin in your panel (app/Providers/Filament/*PanelProvider.php):

->plugin(\Rawand201\FilamentSpatieLighthouse\LighthousePlugin::make())

Publish the config (php artisan vendor:publish --tag="filament-spatie-lighthouse-config" — check the tag, it isn't listed in the notes) and confirm:

  • Chrome/Chromium is installed on the host or in the container — without it, every audit fails at the driver level and you'll chase the wrong problem in the UI.
  • A queue worker is running if you plan to queue audits (highly recommended; a Lighthouse run is 20–60s+).
  • APP_URL is reachable from the server itself if you audit your own app.

First useful workflow: add your staging URL, run a single synchronous audit on the mobile form factor from the Filament page, and confirm you get all four category scores. Only after that turns green should you wire up scheduling or notifications — you want to isolate "can this host run Lighthouse at all" from "is my config right".

Look first at the config file (form factor, thresholds, result store, scheduling, notifications) and the Audit*Event classes — those are the two surfaces you'll touch most. Ignore the API and raw filesystem storage until you need history at scale.

Implementation Patterns

Result store choice is the first architectural decision. Pick deliberately:

  • DatabaseResultStore — default for most apps. Gives you history, per-URL trend, bulk export, pruning. Requires the migration and a scheduled model:prune (or the plugin's pruning) to avoid bloat.
  • CacheResultStore — for stateless/read-only deployments or CI-like usage where you don't care about history. Results evaporate on cache:clear and deploys. Don't use it if anyone will ask "what was the score last Tuesday?".
  • Custom ResultStore — implement the interface when you have a non-standard backend (S3, multi-tenant sharding, an existing metrics warehouse). Bind it in a service provider; the UI, commands and API all resolve through the container.

Run audits from the UI for exploration, from the queue for everything else. The sync button is great for a one-off sanity check. For anything recurring or user-triggered from a live app, queue the audit — otherwise you'll hit PHP max_execution_time, and in Filament that surfaces as a hung spinner rather than a clean error.

Gate deployments on lighthouse:list in JSON. This is the pattern that pays for the package fastest. In CI, after deploy to staging:

php artisan lighthouse:audit --url=$STAGING_URL --sync --queue=default 2>/dev/null || true
php artisan lighthouse:list --format=json | jq -e 'all(.[]; .performance >= 0.85 and .accessibility >= 0.95)'

Use the config thresholds to define pass/fail in one place rather than hard-coding numbers in CI — keep the config and the CI script aligned, and remember thresholds are per-category and per-metric. If you mix form factors, keep separate threshold sets (a mobile Performance of 0.85 is a different beast from desktop).

Scheduled regression monitoring. Enable scheduling.urls in config and let lighthouse:schedule do the fan-out, or call lighthouse:audit explicitly in your Kernel/routes/console.php. Pair this with notifications:

  • Use email for a daily digest to the owning team.
  • Use Slack for failures (the AuditFailedEvent path is what you want here — a "failed to run" is usually a broken Chrome/network, whereas a low score is a AuditEndedEvent with failing thresholds).

Custom listeners for anything beyond built-in notifications. AuditStartingEvent, AuditEndedEvent, AuditFailedEvent are your extension points. Register listeners in EventServiceProvider (or bootstrap/providers.php if you've moved). Keep listeners thin — they run inside the queue worker after the browser has already run, so an accidental HTTP call to a slow webhook blocks the next audit. The natural fan-out is: event → your own job → PagerDuty/Slack/data warehouse. Because events fire for both queued and sync runs, don't assume app()->runningInConsole() is false.

Custom headers for gated environments. This is the killer feature for staging audits behind HTTP basic auth or a WAF. Set an Authorization header or cookie in the audit request config, and audit the protected URL as if from the outside. Combine with a custom user agent so your Cloudflare/WAF rules can allowlist the auditor explicitly rather than fighting bot protection.

API usage (opt-in, token-protected). Enable it only when something external (a dashboard, a deploy pipeline outside Laravel) needs to kick off audits or read results. Protect the token like any other secret, put the routes behind your normal middleware stack (auth, throttle), and prefer tokens scoped to "read results" vs "trigger audit" if the package supports it — if not, at least rotate on a schedule and never expose the routes on the public internet without a rate limit. A Lighthouse audit is an expensive, browser-spawning request; an unthrottled API endpoint is a DoS waiting to happen.

Raw results storage. Set raw_results_driver=filesystem as soon as you have more than a handful of URLs on a schedule. The raw JSON is large, and it's the single biggest source of DB bloat here. Keep the scores and metrics in the DB (they're small and queryable) and the raw payload on disk — this preserves the "history per URL" and export features while keeping the DB lean. Make sure the disk is backed up or expendable, and that the retention policy on disk matches your DB pruning.

Language overrides. For anything user-facing, publish resources/lang/en/lighthouse.php and override rather than wrapping the package in your own translations — this keeps upgrades clean.

Gotchas and Tips

  • No Chrome = no audits, and the error is often indirect. Spatie Lighthouse shells out to a headless Chromium; if it's missing, you'll see driver/launch exceptions, timeouts, or empty results long before a helpful "install Chrome" message. Bake Chromium into your Docker image (with the right --no-sandbox / shared memory flags for containers) and test the container, not your local machine. On managed hosts without root, this may be a blocker — check before promising the feature to anyone.
  • Queued audits need a worker and a timeout budget. Set --timeout on the queue worker comfortably above the slowest Lighthouse run (network throttling + mobile emulation can push a page past 60s). A worker with --timeout=60 and an audit that takes 70s will kill the job after Chrome has already started, wasting the run and leaving orphaned processes.
  • History vs statelessness is a one-way door. CacheResultStore feels convenient in staging, then someone asks for a 30-day trend and you're re-architecting. Choose DatabaseResultStore if "history" is in anyone's mental model of the feature.
  • Pruning doesn't run itself. DatabaseResultStore growth is real and monotonic. Wire model:prune into the scheduler (or a dedicated lighthouse:prune if exposed — check) and verify it actually runs in production, not just locally. Pruning silently not running is the most common "why is this table 4GB" ticket.
  • Bulk export and per-record HTML reports can be heavy on memory. Bulk CSV/JSON over thousands of records will load results into memory. For large datasets, prefer the API or lighthouse:list --format + your own cursor-based exporter rather than the UI button.
  • Thresholds and failures are different. A failed audit means Lighthouse itself couldn't run (Chrome crashed, URL unreachable, TLS error). A low score is a successful audit that breaches your thresholds. Conflating the two leads to noisy Slack alerts; route them separately (AuditFailedEvent → on-call/infra, AuditEndedEvent + threshold breach → owning team's channel).
  • Form factor and throttling are not cosmetic. Mobile + 4G throttling is what Search Console and page-experience reporting reflect; desktop + no throttling flatters you. Pick the pairing that matches what your users experience and keep it consistent across scheduled runs, CI, and ad-hoc audits — a mixed set makes trends meaningless. If you need both, store and chart them as separate series rather than alternating.
  • Events fire in the queue worker. Listeners, notification jobs, and anything they dispatch all run inside the worker context after the audit. A slow listener delays every subsequent queued audit. Queue your listeners (ShouldQueue), or at least keep them under a second.
  • Register the plugin per panel. In a multi-panel Filament app, registering in one PanelProvider does not give you the page in another. Copy-paste errors here produce a silent "the page just isn't there" bug.
  • API token management is opt-in, not secure-by-default. The release notes say "token-protected", but the routes are still new attack surface. Confirm the middleware registration, confirm the token comparison is constant-time (or wraps Laravel's), and confirm the endpoint can't be reached without auth/throttle in your app's real middleware stack — a package route may sit outside your web group.
  • Custom store implementations must be idempotent. Filament page, commands, API, and scheduled runs all write through the same interface; if your implementation assumes "one write per audit" you'll get duplicates when a queued job retries.
  • Filesystem raw storage needs a disk config and a cleanup policy. Without an explicit retention policy on the disk, raw_results_driver=filesystem just moves the bloat from MySQL to your storage bucket. Pair it with pruning so the disk and the DB stay in sync — otherwise your per-record HTML report view will 404 on old records even though the score row still exists.
  • Version pin. This is v1.0.0 of a young package — pin exact minor versions in composer.json and re-read release notes before upgrading. Anything that touches migrations or the Filament page will likely be a breaking change on the 0.x → 1.x boundary, and there's no long support window to fall back on.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast