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

Laravel Sse Laravel Package

sarfraznawaz2005/laravel-sse

A lightweight Laravel package for Server-Sent Events (SSE) that lets you stream real-time updates from your app to the browser without WebSockets, using a simple API for sending events and keeping connections alive.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require sarfraznawaz2005/laravel-sse — 3.0.0 is Laravel 12 only (the sole change in this release is bumping illuminate/support to ^12.0). If you are still on Laravel 10/11, stay pinned to ^2.3; there is no code-level reason to upgrade beyond the framework constraint.

Discovery is automatic via the package's service provider — there is nothing to publish or register for the basic case. Look first at:

  1. The package README's minimal example (the fluent SSE builder is the entire public API surface).
  2. A single routes/web.php route you can hit with curl -N before wiring any frontend.
  3. The facade/class itself (vendor/sarfraznawaz2005/laravel-sse/src) — it is a thin, readable wrapper around response()->stream(), so if an option is missing you can extend it rather than fight it.

First use case — a heartbeat/notification endpoint:

use Sarfraznawaz2005\SSE\Facades\SSE;

Route::get('/stream', function () {
    return SSE::start()
        ->setRetry(3000)
        ->addEvent('ping', ['at' => now()->toDateTimeString()]);
});

Consume it from a plain Blade page with the native browser API; do not add a JS dependency until you actually need reconnection logic:

<script>
const es = new EventSource('/stream');
es.addEventListener('ping', e => console.log(JSON.parse(e.data)));
es.onerror = () => { /* EventSource auto-reconnects using the retry value */ };
</script>

Verify with curl -N -H 'Accept: text/event-stream' http://localhost:8000/stream — you should see framed event:/data: lines and no HTML.

Implementation Patterns

Builder-per-tick inside a loop. The package is a formatter, not a scheduler. The idiomatic pattern is a while loop that builds and flushes one event per iteration:

Route::get('/orders/{order}/stream', function (Order $order) {
    return SSE::start()->setRetry(5000)->stream(function () use ($order) {
        while (true) {
            if (connection_aborted()) break;
            // read from cache/queue, emit one event
            yield ['event' => 'status', 'data' => ['status' => $order->fresh()->status]];
            sleep(2);
        }
    });
});

(Exact fluent method names differ slightly across versions — the README example is authoritative; the shape above is stable.)

Cross-request message passing. A streaming request blocks its PHP worker, so you cannot push from another request directly. Use a store as the channel: the writer does Cache::put("stream:{$userId}", $payload, 60) or Redis::publish(), and the streaming loop polls/blpops it. Prefer Redis BLPOP over sleep(1) polling — it cuts latency and DB load.

Route model + auth. SSE routes still run web middleware, so session auth works — but EventSource cannot send custom headers. Either use a short-lived signed URL (URL::temporarySignedRoute) and signed middleware, or accept a token as a query param and validate it manually. Never rely on Authorization: Bearer from a browser EventSource.

Extract the payload generator. Put "what should I send next" into a small service class and let the route do nothing but format and flush. This is the only part you can meaningfully unit test, and it keeps the streaming loop to ~10 lines.

Lifecycle hooks. Subscribe in the loop to your own Laravel events via a queue/cache-backed listener rather than firing domain events directly inside the stream — that keeps the streaming request read-only.

Frontend composables. For Inertia/Vue/React, wrap EventSource in one composable that owns reconnect, Last-Event-ID bookkeeping (use setId() server-side to make resume work), and cleanup in onUnmounted — leaked EventSource instances are the most common production bug with this pattern.

Gotchas and Tips

  • This is a constraint-driven major. No public API changes between 2.3.0 and 3.0.0. Your upgrade risk is entirely Composer resolution: other packages still requiring illuminate/support ^10|^11 will deadlock with 3.0.0. Run composer why illuminate/support before bumping.
  • Stray output corrupts the stream. A BOM, a blank line before <?php, a dd(), or a middleware that echoes anything will break framing silently — the browser just fires onerror forever. If events "don't arrive", curl -N and diff the bytes before debugging anything else.
  • Disable buffering at every layer. Nginx: X-Accel-Buffering: no (or proxy_buffering off); Apache: SetEnv no-gzip 1 and disable mod_deflate for the route; Cloudflare will buffer unless the response is text/event-stream and large enough — test through the real proxy, not just artisan serve.
  • Release the session lock first. Laravel's session file driver holds an exclusive lock for the whole request. Call session()->save() immediately before entering the loop, or every other request from that user hangs until the stream closes.
  • Worker starvation. Each open stream ties up one PHP-FPM child. Ten users on a 10-worker pool = dead site. Mitigate with set_time_limit(0) + ignore_user_abort(true) server-side, an early connection_aborted() check client-side, a hard max duration (e.g. 5 min) so connections recycle, and a dedicated FPM pool for stream routes.
  • Memory grows quietly. Accumulating arrays, Eloquent models, and query logs inside an endless loop leak. Re-query with ->fresh() or use DB::connection()->disableQueryLog(), and break out of the loop after a bounded number of iterations.
  • Testing. Don't assert on streamed content through the HTTP test client — it will hang or buffer. Test the payload service in isolation and only smoke-test the route's headers (Content-Type: text/event-stream, Cache-Control: no-cache).
  • Extension point. If you need custom framing (comments, retry per-event, binary), subclass the package class or bypass it with response()->stream() — the package adds little beyond header defaults, so there is no lock-in.
  • Telescope/Debugbar in local dev can inject into responses; exclude the stream route from their watchers while debugging.
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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource