Skip to content

Instantly share code, notes, and snippets.

  • Save sidigi/ee16957c362b77142023023be77d76ef to your computer and use it in GitHub Desktop.
Save sidigi/ee16957c362b77142023023be77d76ef to your computer and use it in GitHub Desktop.
Common Problems faced while setting up private channels with laravel-echo & laravel-echo-server.
I'll start with the basics and proceed to addressing the common problems
faced while setting up private channels with laravel-echo & laravel-echo-server.
If you are getting these errors while setup; 401, 403, 419 etc, as I did in my experience.
this gist will help you fix these errors.
Although this gist addresses common problems of laravel-echo-server setup, some problems are similar with Pusher setup.
So it might also be useful if you're having problems with setting up Pusher with Echo.
I'll try to cover eveything and try to use appropriate highlighting to single out each common problem.
I use '---' to separate each section or common problem.
---
NPM Install:
npm install dotenv --save
npm install laravel-echo --save
npm install laravel-echo-server --save
npm install laravel-echo-server --save -g
note that I've installed 'laravel-echo-server' globally and locally in the project.
Global installation will allow us to use cli commands for this package, like 'laravel-echo-server init'
Local installation because of this good practice: https://medium.com/@titasgailius/running-laravel-echo-server-the-right-way-32f52bb5b1c8
---
first and foremost, make sure you have csrf meta tag in the html <head>. Otherwise you'll get 419 error while listening to channels
<meta content="{{ csrf_token() }}" name="csrf-token">
---
In config/app.php
Comment out the line below
App\Providers\BroadcastServiceProvider::class,
---
In .env, you must set these variables:
APP_URL=http://your_domain.com
// common mistake: be carefull while setting the scheme 'http' or 'https' in both local and production environments.
// this mistake can cause 403, 404 error or an error without a status
APP_NAME=YourAppName
// I'd suggest you use a clean name without underscores, hyphens, periods or any symbols.
// errors: 401 403
JWT_SECRET=random_string
JWT_TTL=1440
// set these only if you are using this package for jwt authentication https://github.com/tymondesigns/jwt-auth
CACHE_DRIVER=redis // optional but I'd suggest redis
SESSION_DRIVER=redis // optional but I'd suggest redis
QUEUE_CONNECTION=redis // this refers to the config/queue.php file
BROADCAST_DRIVER=redis
Important Note:
your Redis installation has 0 to 16 DBs
If you plan to use redis as CACHE_DRIVER, SESSION_DRIVER and BROADCAST_DRIVER -- which I'd certainly suggest -- you must use different DB for each driver.
It is widely reported by many laravel developers that redis pub/sub for BROADCAST_DRIVER is affected if a single redis db is used.
To use different DBs make the below entries in .env, each of these we'll set in config/database.php as redis connections.
REDIS_DB=0
REDIS_CACHE_DB=1
REDIS_BROADCAST_DB=2
// error: events are published to channels but echo-server won't listen
---
In config/database.php, at the bottom there is 'Redis Databases' section in which redis's 'default' & 'cache' connections are defined like this
'default' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_DB', 0),
],
'cache' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_CACHE_DB', 1),
],
// add 'broadcast' db connection after 'cache' like this:
'broadcast' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_BROADCAST_DB', 2),
],
---
In app\Providers\BroadcastServiceProvider.php, make sure the boot function has Broadcast::routes() and requires 'routes/channels.php'
public function boot()
{
Broadcast::routes(); // by default these routes use middleware 'auth:web'
// Broadcast::routes(['middleware' => ['auth:api']]); // for auth:api
// Broadcast::routes(['middleware' => ['jwt.auth']]); // for jwt package https://github.com/tymondesigns/jwt-auth
require base_path('routes/channels.php');
}
---
In config/database.php, at the bottom under redis db settings if you have: redis -> options -> prefix
Then all your channels that you brodcast on will have a prefix 'YourAppName_database_' or 'laravel_database_'
for example: YourAppName_database_mymessageschannel
'redis' => [
'client' => env('REDIS_CLIENT', 'predis'),
'options' => [
'cluster' => env('REDIS_CLUSTER', 'predis'),
'prefix' => Str::slug(env('APP_NAME', 'laravel'), '_').'_database_',
// You can customize the prefix however you want, but keep in mind that while listening to the channles you'll have to use this prefix
// error: events are published to channels but echo-server won't listen
],
...
]
---
use 'php artisan make:event EventName' to make an event that should be broadcast. The new Event will be placed in 'app\Events' directory.
I used 'php artisan make:event MessageSent'
then in app\Events\MessageSent
<?php
namespace App\Events;
use App\Message;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class MessageSent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $message;
public function __construct(Message $message)
{
//
$this->message = $message;
//
$this->dontBroadCastToCurrentUser(); // if you don't want to broadcast to the user who generated the event.
}
public function broadcastOn()
{
return new PrivateChannel("room." . $this->message->room_id);
// Very very Important Note: because it sucked a lot of my time
// If you have a redis prefix setup in config/database.php, which I have discussed in the previous section,
// you'll have to use the prefix while listening to the channel in Laravel Echo.
// for example: if you have prefix 'YourAppName_database_', the resulting channel will be 'YourAppName_database_room.{room_id}'
// error: 401, 403
}
}
---
In routes/channels.php
Broadcast::channel('room.{id}', function ($user, $id) {
return (int) $user->room_id === (int) $id;
},
['guards' => ['api']]); // you can optionally give guards as the third agument, if you are using a guard other than the 'web' guard.
// the user must be logged in to authenticate private channels, laravel will automatically place $user as the current logged in user.
// Important Note: Again, if there's a redis prefix setup, as I have discussed in the section above you must prefix it here
// Broadcast::channel('YourAppName_database_room.{id}', function ($user, $id) {
// 'YourAppName_database_' is the redis prefix.
// error: 401, 403
---
Do 'npm install --save socket.io-client'
In resources\...\bootstrap.js, configure import and configure Laravel Echo for laravel-echo-server
import Echo from 'laravel-echo'
window.io = require('socket.io-client');
window.Echo = new Echo({
broadcaster: 'socket.io',
host: window.location.hostname + ':6001'
});
---
// make echo.js in the project directory (where .env is) and copy paste below code and save
// here we use the locally installed 'laravel-echo-server' package
// dotenv will help you get variables from .env file so you'll have a single environment file for both laravel and laravel-echo-server
// after you save the code, you can open a terminal in the project directory and run 'node echo' to start laravel-echo-server
require('dotenv').config();
const env = process.env;
require('laravel-echo-server').run({
authHost: env.APP_URL,
devMode: env.APP_DEBUG,
database: "redis",
databaseConfig: {
redis: {
host: env.REDIS_HOST,
port: env.REDIS_PORT,
family: 4,
db: env.REDIS_BROADCAST_DB // 'REDIS_BROADCAST_DB' is the separate db we have setup in the previous section for broadcast channels
}
}
// more laravel-echo-server options can be added, see it's documentation
});
---
In your vue component, where you'd like to listen to your channel
forexample: resources\js\components\ExampleComponent.vue
mounted () {
var that = this
// if you are using jwt auth, you must set the following headers of window.Echo options to get authenticated for private channels and you must have a mechanism to get the jwt token
// window.Echo.connector.options.auth.headers['Authorization']
// window.Echo.options.auth
// if you are not using jwt, skip the two lines below
window.Echo.connector.options.auth.headers['Authorization'] = 'Bearer ' + jwt_token
window.Echo.options.auth = {
headers: {
Authorization: 'Bearer ' + jwt_token,
},
},
window.Echo.private(`room.${that.room.id}`) // *
.listen('MessageSent', function (e)
{
// doStuff
})
// *
// Important Note: Again, if there's a redis prefix setup, as I have discussed in the previous sections you must prefix it here
// window.Echo.private(`YourAppName_database_room.${that.room.id}`)
// the channel name becomes YourAppName_database_room.{room_id}
// 'YourAppName_database_' is the redis prefix.
// error: 401, 403
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment