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

Larafirebase Laravel Package

kutia-digital-agency/larafirebase

Larafirebase is a Laravel package for sending Firebase Cloud Messaging push notifications via a simple API. Target devices, topics, or conditions with notification and data payloads, making Firebase integration in Laravel quick and clean.

View on GitHub
Deep Wiki
Context7

Getting Started

kutia-software-company/larafirebase is a thin Laravel wrapper around the Firebase Cloud Messaging HTTP v1 API. It gives you a fluent facade for building a message payload and dispatching it to device tokens, topics or topic conditions — without pulling in the full kreait/firebase-php SDK.

Release 1.3.9 is a compatibility/metadata release: it adds Laravel 11, 12 and 13 support (PRs #70, #73, #87, #88) and introduces three new contributors. There are no API changes, no deprecations and no config changes in this release. Treat the upgrade from 1.3.8 as low risk.

First steps:

  1. Install and pin the new floor if you are on a modern framework:
    composer require kutia-software-company/larafirebase:"^1.3.9"
    
    The ^1.3.9 floor matters — 1.3.8 and below will fail dependency resolution on Laravel 11+ because the illuminate/* constraints were not widened there.
  2. Publish the config (tag name varies slightly by version; check php artisan vendor:publish output for the larafirebase tag) and inspect config/larafirebase.php. The important keys are the path to the service account JSON, the API endpoint/project, and proxy/timeout settings.
  3. Provision credentials. Download a service account key from the Firebase console (Project settings → Service accounts → Generate new private key) and place it outside the web root, e.g. storage/app/firebase/service-account.json. Add that path to .env rather than committing it.
  4. Make the first call. The canonical first use case is a transactional push triggered by a domain event:
    use Kutia\Larafirebase\Facades\Larafirebase;
    
    Larafirebase::withTitle('Order shipped')
        ->withBody("Order #{$order->id} is on its way")
        ->sendMessage($user->deviceTokens->pluck('token')->all());
    
    Verify the exact fluent method names against the package README for your pinned version — the recipient method (sendMessage, sendToTopic, sendToCondition) and the payload setters (withTitle, withBody, withData, withAdditionalData, withImage, withSound) have been stable for a long time but the surface has grown incrementally.

Where to look first in the source: src/Larafirebase.php (the fluent builder), src/Services/FirebaseService.php or equivalent (the HTTP/OAuth layer), and config/larafirebase.php. There is very little code; reading it end-to-end takes minutes and answers most questions the README does not.

Implementation Patterns

Wrap the facade in a single application-level service. Do not sprinkle Larafirebase:: calls across controllers and listeners. Create App\Services\PushNotifier that owns payload construction and returns a normalised result object. Benefits: one place to add logging, one place to handle token pruning, and an easy seam for faking in tests.

One queued job per logical message, not per token. Push sending is I/O-bound (see the 500-recipient / one-request-per-message note below). Dispatch a job from your domain event and let the queue absorb latency:

final class SendPushNotification implements ShouldQueue
{
    use Queueable, InteractsWithQueue;

    public int $tries = 3;
    public array $backoff = [10, 60, 300];

    public function __construct(
        public readonly array $tokens,
        public readonly string $title,
        public readonly string $body,
        public readonly array $data = [],
    ) {}
}

Chunk recipient lists. For a fan-out to thousands of devices, chunk into batches of ~500 tokens per job so a single failure does not lose the whole audience and retries stay cheap.

Store device tokens relationally. A device_tokens table with a polymorphic tokenable relation (user, guest session, etc.) plus platform, last_seen_at, and invalidated_at columns. Prune on failure rather than trying to validate tokens up front — FCM tells you when a token is dead, and that is the only reliable signal.

Prefer topics for broadcast, tokens for transactional. Topic sends (sendToTopic) survive token churn and are ideal for announcements. Token sends are the right tool for "your order shipped". Combine them with a subscription-management flow that calls the package's subscribe/unsubscribe methods whenever a user enables or disables a notification category.

Integrate with Laravel's notification system via a custom channel. The most maintainable pattern is a FirebaseChannel in your own app that resolves the notification's toFirebase($notifiable) payload and calls your PushNotifier. This keeps push alongside mail/database channels and keeps the package out of your domain code.

Multi-tenant / multi-project setups. If you push to several Firebase projects, avoid publishing the config twice. Instead swap the credential path at runtime in the service constructor:

config(['larafirebase.path_to_service_account_json' => $tenant->firebase_credentials_path]);

Do this before the facade resolves, or construct a fresh instance, because config is read at send time in most implementations — verify this against the version you pin, since long-running workers will cache whatever you set last.

Testing strategy. Push sending sits behind a queue in real apps, so most tests should be Queue::fake() + assertPushed(SendPushNotification::class). For the unit test of the notifier itself, either bind a fake implementation of your PushNotifier interface, or — if the package uses Guzzle directly — inject a Guzzle MockHandler into the underlying client. Http::fake() only works if the package resolves Laravel's HTTP client; confirm before relying on it. Keep a tiny opt-in integration test that sends to a real test device token, gated behind an env flag, so credential/endpoint breakage is caught in CI rather than production.

Gotchas and Tips

  • Version pinning is the real story of 1.3.9. If composer update silently keeps you on 1.3.8, Laravel 12/13 installs will conflict. Check composer show kutia-software-company/larafirebase after upgrading.
  • "Laravel 13 support" is a forward-looking constraint. The PR widens illuminate/* in composer.json; it does not mean the package has been exercised against an unreleased framework. If you are on the bleeding edge, run your own smoke test against a real FCM project.
  • Data payload values must be strings. FCM silently rejects or coerces non-string values (int, bool, nested arrays) in the data map. Cast at the boundary: array_map('strval', $data).
  • The HTTP v1 API has no true multicast. Each recipient is a separate request. 1,000 tokens means 1,000 HTTP round-trips, which is why chunking + queueing is non-negotiable at scale. This is also the most common cause of "push is slow" reports.
  • Token pruning. FCM returns messaging/registration-token-not-registered (or a 404 with UNREGISTERED). Catch the exception, mark the token invalid, and move on. Do not retry these — a retry budget spent on dead tokens is a retry budget not spent on transient 5xx errors. Classify errors before letting the queue retry: retry 5xx/timeouts, never retry 400/401/404.
  • config:cache will bake in the credentials path. Because the path is usually expressed with storage_path() or env() inside the config file, a cached config deployed to a container with a different filesystem layout will point at nothing. Set the path explicitly per environment and re-run php artisan config:cache on deploy.
  • Service account JSON must be a file path, not a JSON string. Read failures surface as an opaque auth exception. Guard with a file_exists() + is_readable() check in a boot-time health check, and keep the file mode 0600.
  • OAuth token handling in long-running processes. The access token is obtained from the service account and is short-lived. Under Octane, Horizon or a long-lived worker, watch for stale-client or stale-config behaviour; if you see intermittent 401s, resolve the facade fresh per job rather than holding it in a singleton.
  • iOS needs APNs configuration the fluent API may not expose. Sound, badge, content-available, and apns-priority live under the apns key of the message envelope. If the fluent setters do not cover your case, use the raw-payload escape hatch (fromRaw or equivalent) rather than forking the package — check the version's README for the exact method name.
  • Topic naming rules. Use the bare topic name (news), not the /topics/news subscription form. Topic names are case-sensitive and allow only [a-zA-Z0-9-_.~%]. Conditions are capped at five topics per expression.
  • Debugging a failed send. The Google error body is far more informative than the package's exception message. Log the full response body, and temporarily wire up a Guzzle Middleware::log() in a local service provider — the package's proxy/timeout config keys are the natural place to hook a custom client if you need finer instrumentation.
  • Timeout and proxy keys. If your infrastructure requires an egress proxy, set it in the config rather than at the Guzzle default level, so the setting survives package upgrades. Default timeouts are generous; tighten them in a queue context so a hung request does not pin a worker.
  • Extension point. The cleanest extension seam is the fluent builder: if you need a payload shape the API does not cover, macro() the facade or build a small subclass that adds your setters and forwards to the raw payload. Avoid editing vendor code — this package moves often enough that a fork will drift.
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