{"id":21156959,"url":"https://github.com/qruto/laravel-wave","last_synced_at":"2025-05-14T09:09:25.161Z","repository":{"id":51718622,"uuid":"498513849","full_name":"qruto/laravel-wave","owner":"qruto","description":"Painless Laravel Broadcasting with SSE","archived":false,"fork":false,"pushed_at":"2025-03-21T17:42:32.000Z","size":697,"stargazers_count":802,"open_issues_count":0,"forks_count":33,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-04-11T19:14:08.191Z","etag":null,"topics":["broadcasting","laravel","live-update","real-time","sse"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/qruto.png","metadata":{"funding":{"github":"qruto","custom":"send.monobank.ua/jar/3eG4Vafvzq"},"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-05-31T22:16:15.000Z","updated_at":"2025-04-05T12:01:51.000Z","dependencies_parsed_at":"2023-01-31T08:01:14.597Z","dependency_job_id":"6ce613e1-0b9a-414d-8bd6-2e4416e03c38","html_url":"https://github.com/qruto/laravel-wave","commit_stats":{"total_commits":226,"total_committers":4,"mean_commits":56.5,"dds":0.5265486725663717,"last_synced_commit":"cbc5677ef23937508651d7888f00f8c12602d875"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qruto%2Flaravel-wave","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qruto%2Flaravel-wave/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qruto%2Flaravel-wave/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qruto%2Flaravel-wave/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/qruto","download_url":"https://codeload.github.com/qruto/laravel-wave/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248465345,"owners_count":21108244,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["broadcasting","laravel","live-update","real-time","sse"],"created_at":"2024-11-20T11:56:13.292Z","updated_at":"2025-04-11T19:14:16.827Z","avatar_url":"https://github.com/qruto.png","language":"PHP","funding_links":["https://github.com/sponsors/qruto","send.monobank.ua/jar/3eG4Vafvzq"],"categories":["PHP"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003cpicture\u003e\n        \u003csource \n            width=\"350\" \n            media=\"(prefers-color-scheme: dark)\"\n            srcset=\"https://github.com/qruto/laravel-wave/raw/HEAD/art/logo-dark.png\"\n        \u003e\n        \u003csource\n            width=\"350\"\n            media=\"(prefers-color-scheme: light)\"\n            srcset=\"https://github.com/qruto/laravel-wave/raw/HEAD/art/logo-light.png\"\n        \u003e\n        \u003cimg\n            alt=\"Laravel Wave Logo\"\n            src=\"https://github.com/qruto/laravel-wave/raw/HEAD/art/logo-light.png\"\n            width=\"350\"\n        \u003e\n    \u003c/picture\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003eBring \u003cstrong\u003elive\u003c/strong\u003e to your application\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/qruto/laravel-wave/actions/workflows/tests.yml\"\u003e\u003cimg src=\"https://github.com/qruto/laravel-wave/actions/workflows/tests.yml/badge.svg\" alt=\"Build Status\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/qruto/laravel-wave/actions/workflows/styles.yml\"\u003e\u003cimg src=\"https://github.com/qruto/laravel-wave/actions/workflows/styles.yml/badge.svg\" alt=\"Styles check\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/qruto/laravel-wave/actions/workflows/types.yml\"\u003e\u003cimg src=\"https://github.com/qruto/laravel-wave/actions/workflows/types.yml/badge.svg\" alt=\"Types check\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/qruto/laravel-wave/actions/workflows/refactor.yml\"\u003e\u003cimg src=\"https://github.com/qruto/laravel-wave/actions/workflows/refactor.yml/badge.svg\" alt=\"Refactor code\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://packagist.org/packages/qruto/laravel-wave\"\u003e\u003cimg src=\"https://img.shields.io/packagist/dt/qruto/laravel-wave\" alt=\"Total Downloads\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://packagist.org/packages/qruto/laravel-wave\"\u003e\u003cimg src=\"https://img.shields.io/packagist/v/qruto/laravel-wave\" alt=\"Latest Stable Version\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003cpicture\u003e\n        \u003csource \n            media=\"(prefers-color-scheme: dark)\"\n            srcset=\"https://github.com/qruto/laravel-wave/raw/HEAD/art/connection-demo-dark.png\"\n        \u003e\n        \u003csource \n            media=\"(prefers-color-scheme: light)\" \n            srcset=\"https://github.com/qruto/laravel-wave/raw/HEAD/art/connection-demo-light.png\"\n        \u003e\n        \u003cimg\n            alt=\"Laravel Wave Demo\"\n            src=\"https://github.com/qruto/laravel-wave/raw/HEAD/art/connection-demo-light.png\"\n            width=\"400\"\n        \u003e\n    \u003c/picture\u003e\n\u003c/p\u003e\n\n# Introduction\n\nUnlock the power of\nLaravel's [broadcasting system](https://laravel.com/docs/master/broadcasting)\nwith **Wave**. Imagine that real-time server broadcasting is possible over\nnative HTTP without any ~WebSockets~ setup.\nMeet the\n**[Server-sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events)** 🛜\nWorks seamlessly with Laravel's default `redis` broadcasting driver and\nsupports [Laravel Echo](https://github.com/laravel/echo).\n\nExperience it live with our [demo streaming tweets](https://wave.qruto.dev) 🐤.\n\n\u003e Server-Sent Events (**SSE**) is specially tuned for real-time server-to-client\n\u003e communication.\n\n_Compatible\nwith_ [\u003cimg width=\"150\" src=\"https://raw.githubusercontent.com/laravel/echo/master/art/logo.svg\" /\u003e](https://laravel.com/docs/11.x/broadcasting)\n[\u003cimg width=\"150\" src=\"https://raw.githubusercontent.com/laravel/octane/2.x/art/logo.svg\" /\u003e](https://laravel.com/docs/11.x/octane)\n[\u003cimg src=\"https://herd.laravel.com/images/appicon.png\" width=\"40\" /\u003e](https://herd.laravel.com)\n\n## 🌟 Key Features\n\n**⚡ Works with native Redis Driver**: Wave seamlessly integrates with Laravel's\ndefault `redis` broadcasting driver, ensuring efficient real-time data transfer.\n\n**🔄 Resume From Last**: Connection drops? No problem! Wave intelligently resumes\nthe event stream from the last event, ensuring no crucial data is lost in\ntransit.\n\n**🟢 Live Models**: With a simple interface that respects Laravel's native\nconventions\nfor [Model Events Broadcasting](https://laravel.com/docs/master/broadcasting#model-broadcasting)\nand [Broadcast Notifications](https://laravel.com/docs/master/notifications#broadcast-notifications),\nWave turbocharges your application with real-time updates.e\n\n**🍃 Resource-Friendly Broadcasting with `pauseInactive`**: This feature\nmaximizes resource efficiency by closing the data stream when user inactive (\nsuch as when the user minimizes the browser) and automatically reopens it upon\nresumption of visibility. Turned off by default.\n\n**🎛️️ Full Requests Control**: Wave hands you the reins over connection and\nauthentication requests, granting you the freedom to shape your broadcasting\nsetup to your exact requirements.\n\n## Installation\n\n### Laravel 11 or higher\n\nInstall the package via Composer at first, then install broadcasting setup:\n\n```bash\ncomposer require qruto/laravel-wave\nphp artisan install:broadcasting\n```\n\n### Laravel 10 or lower\n\nInstall **Wave** on both server and client sides using Composer and npm:\n\n```bash\ncomposer require qruto/laravel-wave\nnpm install laravel-wave\n```\n\nThen, set your `.env` file to use the `redis` broadcasting driver:\n\n```ini\nBROADCAST_DRIVER = redis\n```\n\n## Usage\n\nAfter installing **Wave**, your server is ready to broadcast events.\nYou can use it with **Echo** as usual or try `Wave` model API to work with\npredefined Eloquent events.\n\nIn Laravel 11 or higher, after `install:broadcasting`, you will find:\n\n- broadcasting channel authorization file in `routes/channels.php`\n- broadcasting configuration file in `config/broadcasting.php`\n- echo instance in `resources/echo.js`\n- _(optional)_ **Wave** configuration file in `config/wave.php`\n\n### Manual usage\n\nImport Laravel Echo with `WaveConnector` and pass it to the broadcaster option:\n\n```javascript\nimport Echo from 'laravel-echo';\n\nimport { WaveConnector } from 'laravel-wave';\n\nwindow.Echo = new Echo({broadcaster: WaveConnector});\n```\n\n\u003cdetails\u003e\n    \u003csummary\u003e\n        For Laravel 10 or lower, locate Echo connection configuration in\n        \u003cem\u003eresources/js/bootstrap.js\u003c/em\u003e file.\n    \u003c/summary\u003e\n\n```diff\n- import Echo from 'laravel-echo';\n\n- import Pusher from 'pusher-js';\n- window.Pusher = Pusher;\n\n- window.Echo = new Echo({\n-     broadcaster: 'pusher',\n-     key: import.meta.env.VITE_PUSHER_APP_KEY,\n-     wsHost: import.meta.env.VITE_PUSHER_HOST ?? `ws-${import.meta.env.VITE_PUSHER_APP_CLUSTER}.pusher.com`,\n-     wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80,\n-     wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443,\n-     forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https',\n-     enabledTransports: ['ws', 'wss'],\n- });\n+ import Echo from 'laravel-echo';\n\n+ import { WaveConnector } from 'laravel-wave';\n\n+ window.Echo = new Echo({ broadcaster: WaveConnector });\n```\n\n\u003c/details\u003e\n\nUse Echo as you typically would.\n\n📞 [Receiving Broadcasts](https://laravel.com/docs/master/broadcasting#receiving-broadcasts)\ndocumentation.\n\n### Use Live Eloquent Models\n\nWith native conventions\nof [Model Events Broadcasting](https://laravel.com/docs/master/broadcasting#model-broadcasting)\nand [Broadcast Notifications](https://laravel.com/docs/master/notifications#broadcast-notifications)\nyou can use\n**Wave** models to receive model events and notifications.\n\n```javascript\nimport { Wave } from 'laravel-wave';\n\nwindow.Wave = new Wave();\n\nwave.model('User', '1')\n    .notification('team.invite', (notification) =\u003e {\n        console.log(notification);\n    })\n    .updated((user) =\u003e console.log('user updated', user))\n    .deleted((user) =\u003e console.log('user deleted', user))\n    .trashed((user) =\u003e console.log('user trashed', user))\n    .restored((user) =\u003e console.log('user restored', user))\n    .updated('Team', (team) =\u003e console.log('team updated', team));\n```\n\nStart by calling the `model` method on the `Wave` instance with the model name\nand key.\n\nBy default, Wave prefixes model names with `App.Models` namespace. You can\ncustomize this with the `namespace` option:\n\n```javascript\nwindow.Wave = new Wave({namespace: 'App.Path.Models'});\n```\n\n📄 [Check out full Laravel Broadcasting documentation](https://laravel.com/docs/11.x/broadcasting)\n\n## Configuration\n\n### Client Options\n\nThese options can be passed to the `Wave` or `Echo` instance:\n\n| Name          | Type                                                                                   | Default                 | Description                                                                                                                          |\n|---------------|----------------------------------------------------------------------------------------|-------------------------|--------------------------------------------------------------------------------------------------------------------------------------|\n| endpoint      | _string_                                                                               | `/wave`                 | Primary SSE connection route.                                                                                                        |\n| namespace     | _string_                                                                               | `App.Events`            | Namespace of events to listen for.                                                                                                   |\n| auth.headers  | _object_                                                                               | `{}`                    | Additional authentication headers.                                                                                                   |\n| authEndpoint  | _string?_                                                                              | `/broadcasting/auth`    | Authentication endpoint.                                                                                                             |\n| csrfToken     | _string?_                                                                              | `undefined` or `string` | CSRF token, defaults from `XSRF-TOKEN` cookie.                                                                                       |\n| bearerToken   | _string?_                                                                              | `undefined`             | Bearer tokenfor authentication.                                                                                                      |\n| request       | _[Request](https://developer.mozilla.org/en-US/docs/Web/API/Request/Request#options)?_ | `undefined`             | Custom settings for connection and authentication requests.                                                                          |\n| pauseInactive | _boolean_                                                                              | `false`                 | If `true`, closes connection when the page is hidden and reopens when visible.                                                       |\n| debug         | _boolean_                                                                              | `false`                 | Toggles debug mode. If set to `true`, provides detailed event logs in the console, helping with event diagnosis and troubleshooting. |\n\n```javascript\nnew Echo({\n    broadcaster: WaveConnector,\n    endpoint: '/sse-endpoint',\n    bearerToken: 'bearer-token',\n    //...\n});\n\n// or\n\nnew Wave({\n    authEndpoint: '/custom-broadcasting/auth',\n    csrfToken: 'csrf-token',\n})\n```\n\n### Server Options\n\nYou can publish the Wave configuration file with:\n\n```bash\nphp artisan vendor:publish --tag=\"wave-config\"\n```\n\nHere are the contents of the published configuration file:\n\n```php\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Resume Lifetime\n    |--------------------------------------------------------------------------\n    |\n    | Define how long (in seconds) you wish an event stream to persist so it\n    | can be resumed after a reconnect. The connection automatically\n    | re-establishes with every closed response.\n    |\n    | * Requires a cache driver to be configured.\n    |\n    */\n    'resume_lifetime' =\u003e 60,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Reconnection Time\n    |--------------------------------------------------------------------------\n    |\n    | This value determines how long (in milliseconds) to wait before\n    | attempting a reconnect to the server after a connection has been lost.\n    | By default, the client attempts to reconnect immediately. For more\n    | information, please refer to the Mozilla developer's guide on event\n    | stream format.\n    | https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format\n    |\n    */\n    'retry' =\u003e null,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Ping\n    |--------------------------------------------------------------------------\n    |\n    | A ping event is automatically sent on every SSE connection request if the\n    | last event occurred before the set `frequency` value (in seconds). This\n    | ensures the connection remains persistent.\n    |\n    | By setting the `eager_env` option, a ping event will be sent with each\n    | request. This is useful for development or for applications that do not\n    | frequently expect events. The `eager_env` option can be set as an `array` or `null`.\n    |\n    | For manual control of the ping event with the `sse:ping` command, you can\n    | disable this option.\n    |\n    */\n    'ping' =\u003e [\n        'enable' =\u003e true,\n        'frequency' =\u003e 30,\n        'eager_env' =\u003e 'local', // null or array\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Routes Path\n    |--------------------------------------------------------------------------\n    |\n    | This path is used to register the necessary routes for establishing the\n    | Wave connection, storing presence channel users, and handling simple whisper events.\n    |\n    */\n    'path' =\u003e 'wave',\n\n    /*\n     |--------------------------------------------------------------------------\n     | Route Middleware\n     |--------------------------------------------------------------------------\n     |\n     | Define which middleware Wave should assign to the routes that it registers.\n     | You may modify these middleware as needed. However, the default value is\n     | typically sufficient.\n     |\n     */\n    'middleware' =\u003e [\n        'web',\n    ],\n\n    /*\n     |--------------------------------------------------------------------------\n     | Auth \u0026 Guard\n     |--------------------------------------------------------------------------\n     |\n     | Define the default authentication middleware and guard type for\n     | authenticating users for presence channels and whisper events.\n     |\n     */\n    'auth_middleware' =\u003e 'auth',\n\n    'guard' =\u003e 'web',\n\n];\n```\n\n## Persistent Connection with Nginx + PHP FPM\n\nWave is designed to automatically reconnect after a request timeout.\nDuring reconnection, you won't lose any events because Wave stores event history\nfor one minute by default\nand resumes it. You can adjust the duration of event history storage by\nmodifying the `resume_lifetime` value\nin the config file.\n\nHowever, if you want to maintain a persistent connection, let's configure your\nweb server.\n\n### `fastcgi_read_timeout`\n\nBy default, the `fastcgi_read_timeout` value is `60s` for Nginx + PHP FastCGI\nserver setup.\n\n#### Option 1. Without changing the `fastcgi_read_timeout` value\n\n\u003e Ensure that the interval between events pushed into Wave connection is shorter\n\u003e than the read timeout value\n\nTo enhance the certainty of events occurring more frequently than the standard\ntimeout,\nWave attempts to send a ping event with each Server-Sent Events (SSE) connection\nrequest,\nprovided that the previous event occurred prior to the `ping.frequency`\nconfiguration value.\n\nIf your application doesn't expect many real-time connections,\nspecify the list of environments in which a ping event will be sent\nwith each Wave connection. By default, this is set to `local`.\n\n#### Option 2. Manual ping control\n\nTo ensure accurate frequency sending a ping event:\n\n1. Disable automatic sending by changing the `ping.enable` config value\n   to `false`\n2. Use the `sse:ping` command to manually send a single ping or operate at an\n   interval\n\nRun the command with the `--interval` option to send a ping event at a specified\ninterval in seconds,\nfor example let's send a ping event every `30s`:\n\n```bash\nphp artisan sse:ping --interval=30\n```\n\nSo, every `30s`, the command will send a ping event to all active connections\nand\nensure that the connection remains persistent, because the frequency of sending\nevents is less than `60s`.\n\nAlternatively, use the\nLaravel [Tasks scheduler](https://laravel.com/docs/master/scheduling#introduction)\nto send a ping event every minute or more often\nif `fastcgi_read_timeout` value is greater than `60s`:\n\n```php\nprotected function schedule(Schedule $schedule)\n{\n    $schedule-\u003ecommand('sse:ping')-\u003eeveryMinute();\n}\n```\n\n### `request_terminate_timeout`\n\nSome platforms, such as [Laravel Forge](https://forge.laravel.com), configure\nthe PHP FPM pool with `request_terminate_timeout = 60`, terminating all requests\nafter `60s`.\n\nYou can disable this in the `/etc/php/8.1/fpm/pool.d/www.conf` config file:\n\n```ini\nrequest_terminate_timeout = 0\n```\n\nor you can configure a separate pool for the SSE connection.\n\n## Future Plans\n\n📍 Local broadcasting driver\n\n📥 📤 two ways live models syncing\n\n📡 Something awesome with opened live abilities...\n\n## Testing\n\n```bash\ncomposer test\n```\n\n## Support\n\nIn light of recent events in Ukraine, my life has taken an unexpected turn.\nSince February 24th, I've lost my commercial work, my permanent residence,\nand my ability to plan for the future.\n\nDuring these challenging times, I derive strength and purpose from creating\nopen source projects, such as Wave.\n\n[![support me](https://raw.githubusercontent.com/slavarazum/slavarazum/main/support-banner.png)](https://github.com/sponsors/qruto)\n\nI welcome you to visit\nmy [GitHub Sponsorships profile](https://github.com/sponsors/qruto).\nThere, you can discover more about my current work, future ambitions, and\naspirations.\nEvery ⭐ you give brings joy to my day, and your sponsorship can make\na profound difference in my ability to continue creating.\n\nI'm truly grateful for your support, whether it's a shout-out or a heartfelt \"\nthank you\".\n\n💳 [Help directly](https://revolut.me/slavarazum).\n\n## Changelog\n\nPlease see [CHANGELOG](CHANGELOG.md) for more information on what has changed\nrecently.\n\n## Contributing\n\nPlease\nsee [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md)\nfor details.\n\n## Security Vulnerabilities\n\nPlease review [our security policy](../../security/policy) on how to report\nsecurity vulnerabilities.\n\n## Credits\n\n- [Slava Razum](https://github.com/slavarazum)\n- [All Contributors](../../contributors)\n\nPackage template based\non [Spatie Laravel Skeleton](https://github.com/spatie/package-skeleton-laravel).\n\n## License\n\nThe MIT License (MIT). Please see [License File](LICENSE.md) for more\ninformation.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqruto%2Flaravel-wave","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fqruto%2Flaravel-wave","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqruto%2Flaravel-wave/lists"}