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

Expo Notifications Bundle Laravel Package

solvecrew/expo-notifications-bundle

A Laravel bundle for Expo push notifications: manage device tokens, send notifications to iOS and Android through Expo, and add configurable push messaging to your app with a simple API.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit — this is the headline finding: the package is a Symfony bundle, not a Laravel package. solvecrew/expo-notifications-bundle is built around Symfony's bundle contract: a *Bundle class, a DependencyInjection\Extension that parses a Configuration tree, service definitions in YAML/XML, and likely autowired constructor injection for its Expo client and receipt-poller. None of that machinery exists in Laravel. Laravel's package ecosystem relies on ServiceProvider / Facade / register()+boot(), and the framework does not read Symfony bundle Extension classes, so installing this via Composer will register classes in the autoloader but will not boot the services, config, or any receipt-checking subscriber. In practice a Laravel team would be adopting a set of plain PHP classes and hand-wiring them.

What it actually does (and why the underlying problem is worth solving). The scope is the server side of Expo push: register/store ExponentPushTokens, batch a send to https://exp.host/--/api/v2/push/send (Expo's ~100-message chunk limit), parse the per-message ticket response, then later call /push/getReceipts to distinguish DeviceNotRegistered tokens from transient failures, and prune dead tokens. That receipt-and-prune loop is the genuinely valuable, commonly-underbuilt part of Expo integration, and the package's abstraction over it is the main argument in its favour.

Integration feasibility for a Laravel/PHP shop: low as a dependency, moderate as a library. Two paths exist: (a) install it and treat it as an unopinionated library — feasible only if its classes avoid tight coupling to Symfony ContainerInterface, Config components, and EventDispatcher, which a bundle of this kind typically does not; or (b) lift the logic. The send path is thin (an HTTP POST with Authorization: Bearer <accessToken> and a JSON array body), and the receipt path is a second POST plus a diff against stored tokens. A senior engineer reimplements both in one to three days with Illuminate\Support\Facades\Http, a notifications jobs table or queue, and a scheduled php artisan command for receipts. That is comparable effort to untangling the bundle's bootstrapping, with full control over retry semantics.

Technical risk.

  • Adoption signal is essentially absent. 20 stars and 0 dependents means no production cohort has hardened this against real Expo API drift, no Stack Overflow corpus, and no third party who has hit your edge case before you.
  • Bus factor of one. A single-maintainer Solvecrew package with MIT licence is fine to read, but you inherit the maintenance contract if you depend on it.
  • Expo API surface is versioned in place. Expo has moved the push API between v2 and later endpoints and changed receipt/TTL semantics; an unmaintained wrapper can pin you to a deprecated shape with no deprecation notices.
  • Low abstraction leverage. The domain is small. You are taking a dependency, a transitive Symfony HTTP client and config-component footprint, and an unfamiliar bootstrapping model, in exchange for a few hundred lines of fairly obvious glue.
  • Security/licence posture is clean but thin. MIT is permissive and compatible with a commercial Laravel codebase; there is no evident security policy or advisory history to review either way.

Key questions to resolve before committing.

  1. Does the bundle's core client class compile and run standalone, or does it require the container to be booted (i.e. does it type-hint ContainerInterface or EventDispatcherInterface)?
  2. Which Expo API version and endpoint does it target, and does it handle the data.status === "error" / DeviceNotRegistered branch of the receipt response, not just the ticket response?
  3. How does it persist tokens? If it ships an entity/repository or Doctrine mapping, that is a hard no for an Eloquent stack.
  4. Does it batch at Expo's 100-message limit, and does it back off on 429 with Retry-After?
  5. Is there a supported path for authenticated Expo access tokens (the enhanced-security project setting), or is it anonymous-only?

Verdict. Technically credible for its stated ecosystem, but the ecosystem is the wrong one. The right move for a Laravel team is to read this package as a reference implementation, not to install it — harvest the receipt-polling design, then build the ~200 lines natively against Laravel's HTTP client and queue, or adopt an actively maintained Laravel-native Expo notification channel if one meets the receipt-handling bar.

Integration Approach

Stack fit. Poor as a Composer dependency in a Laravel application; good as a pattern source. The bundle's assumptions — Symfony DI, bundle config trees, likely a Symfony HttpClientInterface and EventDispatcher — collide with Laravel's container, config repository, queue, and scheduler. There is no illuminate/* bridge in the package and no evidence of Laravel support in its scope statement.

Recommended path: port, don't install. Treat the repository as an architecture reference and reimplement against Laravel primitives. Concretely:

  • Token storage — an expo_push_tokens table (user_id, token, device_id, platform, last_seen_at, revoked_at) with a unique index on token. This replaces whatever persistence the bundle assumes and gives you Eloquent access for pruning.
  • Send — a queued job per chunk of ≤100 messages hitting /--/api/v2/push/send, using Http::withToken(config('services.expo.token'))->retry(3, 200)->post(...). Wrap in a NotificationChannel so it composes with Laravel's Notifiable trait, which the bundle cannot offer.
  • Receipts — an ExpoCheckReceipts artisan command scheduled every 15–30 minutes, keyed off a push_receipts table populated at send time from each ticket's id. This is the piece worth copying carefully: map DeviceNotRegistered → revoke token; MessageTooBig/MessageRateExceeded → log and don't retry blindly; InvalidCredentials → alert, because it means your access token is wrong and every send is silently failing.
  • Config — a single config/services.php entry (expo.token, expo.batch_size, expo.receipt_batch_size) rather than a Symfony Configuration tree.

Migration path if you must evaluate the dependency first. Sequence a two-day spike: (1) composer require solvecrew/expo-notifications-bundle in a throwaway Laravel app and attempt to instantiate the send/receipt service manually with new, no container; (2) if it constructs cleanly, write a thin Laravel ServiceProvider that binds the client singleton and exposes config from services.php; (3) run it against Expo's sandbox push tool to confirm the payload shape and receipt round-trip. If step 1 fails — the likely outcome — stop and port. Do not attempt to register the bundle with Symfony\Component\HttpKernel, and do not pull in symfony/framework-bundle to satisfy it; that is a full parallel framework inside your Laravel app and an unambiguous anti-pattern.

Compatibility checks. PHP version constraint versus your runtime; whether it requires symfony/http-client (which will coexist with Guzzle but means two HTTP stacks and two sets of TLS/timeout semantics in one app); whether it requires doctrine/orm or symfony/serializer transitively; and whether the Expo API version it targets is still live. All four are quick composer why / source-read checks and all four can independently disqualify it.

Sequencing for a greenfield rollout. Deploy token registration and storage first (a POST /push-tokens endpoint, idempotent on token, called from the React Native client on login and on token refresh). Ship sends second, behind a queue, with a feature flag and a single internal test device. Add receipt polling third — explicitly later, not in the same release — because that is where the token-lifecycle bugs surface and you want send-path stability first. Only after receipts are trustworthy should you enable fan-out to real user segments.

Operational Impact

Maintenance burden. Low if ported (~200 lines you own, covered by tests you control); elevated and asymmetric if adopted, because you would be maintaining a Symfony bundle inside a Laravel app — patching its bootstrapping to survive framework upgrades, and absorbing any upstream breakage yourself given 0 dependents and a single maintainer. Budget a standing dependency-review line item, and expect to vendor the source within a year.

Support. No commercial support, no visible Slack/Discussions, 20 stars means effectively no community. Escalation path for a production incident is "read the source and fix it yourself," which is acceptable only if you have already decided the code is small enough to own — in which case porting is strictly cheaper.

Scaling. The Expo push API is the bottleneck, not the package: ~100 messages per request, per-second rate limits, and receipt lookups batched by ticket ID. A naive synchronous send inside a web request will fall over at modest user counts (a 10k-user broadcast at 100/request is 100 sequential HTTPS calls — minutes of latency). Design for a queued, chunked job with concurrency limited by Expo's rate limits, and store receipts in the database so polling is a cursor scan rather than an in-memory list. The package's own scaling story is opaque; assume you own it.

Failure modes to instrument explicitly.

  • Silent total failure — invalid or rotated Expo access token: tickets return InvalidCredentials, sends appear successful at the HTTP layer. Alert on any non-ok ticket status.
  • Dead token accumulation — skipping receipt polling means you keep pushing to uninstalled apps forever, degrading deliverability and inflating cost. Monitor revoke rate; a sudden spike usually means a client bug or a mass uninstall.
  • Ticket/receipt desync — if the process crashes between send and receipt persistence, you have orphaned tickets that will never be checked. Accept the loss and log the count rather than building distributed transactions.
  • Provider outage and retry storms — Expo 503/429 with Retry-After must back off; a naive retry(5) on a queued fan-out multiplies load against an already-degraded provider.
  • Payload size — APNs/FCM reject payloads over the limit per platform; validate notification body size before queueing or you get a per-message error inside a batch that is easy to miss.
  • Token collision on reinstall — same physical device issuing a new token; requires dedupe on device_id or you fan out duplicates to one user.

Ramp-up. For a Laravel engineer with no Expo experience: roughly 1–2 days to read this package plus Expo's push docs and produce a working native implementation, plus 2–4 days to harden receipts, pruning, and observability. For the install-the-bundle path, add an indeterminate amount of time untangling Symfony bootstrapping — the classic outcome is a day spent proving it cannot be wired cleanly, followed by the port anyway. That asymmetry, not the star count, is the decisive argument.

Recommendation. Do not add this as a production dependency. Use it as a design reference to build a Laravel-native Expo channel, or evaluate a maintained Laravel-specific alternative that already implements receipt polling. Reassess only if the maintainer publishes a framework-agnostic core with a documented Laravel adapter.

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.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator