Skip to main content

Black Friday 2025! Only until December 1st: coupon FRIDAY25 for 40% off Yearly/Lifetime membership!

Read more here
Premium Members Only
Join to unlock this tutorial and all of our courses.
Tutorial Premium Tutorial

Laravel Reverb Demo: Real-Time Notification on Completed Task

March 18, 2024
11 min read

Some Laravel tasks are running in the background; you must check whether they are finished. But what if you didn't need to constantly check but rather "listen" for those events to finish? Let's implement exactly this real-time feedback with the Reverb server.

This is our task: allow the user to export some files and tell the user when the file is prepared for download.

In this tutorial, I will show you how to implement it with the official Laravel Reverb tool. There are other alternatives, but we are currently focusing on official Laravel tools.

What we'll cover in this tutorial:

  • Preparing Laravel project
  • Install and Run the Reverb Server
  • Configure Laravel Broadcasting
  • Configure Front-end Client
  • Export Back-End: Job, Event, Controller
  • Export Front-end JS: Button and Status Updates

So, are you ready? Let's dive in!


Preparing Laravel Project

For this tutorial, we will create a fresh Laravel project using the laravel new command. Here are our settings:

We will use Laravel Breeze for our Authentication and UI. Our database will be SQLite, as that is the default for Laravel 11.

Seed Users Demo Data

We need to add our Admin user and some random users by modifying database/seeders/DatabaseSeeder.php:

database/seeders/DatabaseSeeder.php:

class DatabaseSeeder extends Seeder
{
public function run()
{
User::factory()->create([
'name' => 'Test User',
'email' => '[email protected]',
]);
 
User::factory()
->count(1000)
->create();
}
}

Then, we can run the migration and seed the database:

php artisan migrate:fresh --seed

Setup Front-end

Before we dive into the Reverb things, let's set up our base front end. This will include:

  • New route
  • Controller
  • View (users list)

Let's do this quickly:

Controller

app/Http/Controllers/UsersController.php

namespace App\Http\Controllers;
 
use App\Models\User;
 
class UsersController extends Controller
{
public function __invoke()
{
$users = User::query()
->paginate();
 
return view('users.index', compact('users'));
}
}

View

resources/views/users/index.blade.php

<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('Users List') }}
</h2>
</x-slot>
 
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-90 0 dark:text-gray-100">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead class="bg-gray-50 dark:bg-gray-800">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Name
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Email
</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-900 divide-y divide-gray-200 dark:divide-gray-700">
@foreach ($users as $user)
<tr>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
{{ $user->name }}
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-500 dark:text-gray-400">
{{ $user->email }}
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
<div class="mt-4">
{{ $users->links() }}
</div>
</div>
</div>
</div>
</div>
</x-app-layout>

Route

routes/web.php

use App\Http\Controllers\UsersController;
 
// ...
 
 
Route::middleware('auth')->group(function () {
// ...
 
Route::get('users', UsersController::class)->name('users.index');
});

Then, we need to run NPM to compile our assets:

npm run build

Now, if you navigate to <APP_URL>/users, you should see the default table of users with our seeded data:

Note: you can log in with [email protected] / password.

Users table

Ok, preparation is done; now let's build a button to export users with Reverb.


Install and Run the Reverb

To install Reverb, we have to call this command:

php artisan install:broadcasting

This will ask us if...

Premium Members Only

This advanced tutorial is available exclusively to Laravel Daily Premium members.

Premium membership includes:

Access to all premium tutorials
Video and Text Courses
Private Discord Channel

Comments & Discussion

EZ
Eric Zwart ✓ Link copied!

How does that work on a production server? What do you run as cronjob to keep reverb started etc?

M
Modestas ✓ Link copied!

We specifically did not write anything about the production deployment as the documentation has a very nice and detailed guide:

https://laravel.com/docs/11.x/reverb#production

Also, since this might be a bit unclear - it is recommended (at least in my experience) to run it as a command via supervisor/daemon. This ensures that it is running without interuptions and doesn't cause any problems

EZ
Eric Zwart ✓ Link copied!

Thanks!

GK
Gavin Kimpson ✓ Link copied!

Is this getting a public github repo? I seem to be running into various issues - p.s the x-app-layout start tag is missing>

Getting various logs in my logs

` artisan queue:work • default • App\Events\ExportPdfStatusUpdated ┌ 2024-03-23 17:05:35 Illuminate\Broadcasting\BroadcastException vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/PusherBroadcaster.php:164 │ Pusher error:

Not Found

The requested URL was not found on this server.

. │ 1. vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastEvent.php:92 │ 2. vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:36 │ 3. vendor/laravel/framework/src/Illuminate/Container/Util.php:41 │ 4. vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:93 │ 5. vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:35 │ 6. vendor/laravel/framework/src/Illuminate/Container/Container.php:662 │ 7. vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php:128 │ 8. vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:144 │ 9. vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:119 │ 10. vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php:132 │ 11. vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php:123 │ 12. vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:144 │ 13. vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:119 │ 14. vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php:122 │ 15. vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php:70 │ 16. vendor/laravel/framework/src/Illuminate/Queue/Jobs/Job.php:102 │ 17. vendor/laravel/framework/src/Illuminate/Queue/Worker.php:439 │ 18. vendor/laravel/framework/src/Illuminate/Queue/Worker.php:389 │ 19. vendor/laravel/framework/src/Illuminate/Queue/Worker.php:333 │ 20. vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php:139 │ 21. vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php:122 │ 22. vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:36 │ 23. vendor/laravel/framework/src/Illuminate/Container/Util.php:41 │ 24. vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:93 │ 25. vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:35 │ 26. vendor/laravel/framework/src/Illuminate/Container/Container.php:662 │ 27. vendor/laravel/framework/src/Illuminate/Console/Command.php:212 │ 28. vendor/symfony/console/Command/Command.php:279 │ 29. vendor/laravel/framework/src/Illuminate/Console/Command.php:181 │ 30. vendor/symfony/console/Application.php:1049 │ 31. vendor/symfony/console/Application.php:318 │ 32. vendor/symfony/console/Application.php:169 │ 33. vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php:196 │ 34. vendor/laravel/framework/src/Illuminate/Foundation/Application.php:1183 │ 35. artisan:13 └─────────────────────────────────────────────────────────────────────────────────── artisan queue:work • default • App\Events\ExportPdfStatusUpdated
M
Modestas ✓ Link copied!

Sorry, this project does not have a repository.

Based on the error, it seems like you did not install this correctly and your driver is misconfigured. Not sure exactly what happened there

DB
David Bonduel ✓ Link copied!

I do not understand how you only show the button for authenticated users only with the above code. Am i something missing? thanks!

M
Modestas ✓ Link copied!

what do you mean by that? We dont have any code that limits it to only authenticated users (apart from the whole dashboard limited by auth middleware)

DB
David Bonduel ✓ Link copied!

The first part is the button itself: the only thing we do here is display the Export PDF button for authenticated users only

M
Modestas ✓ Link copied!

Ahh, I see. This wording is a bit confusing!

There is no hidden magic, it's just a button there. Will adjust wording!

DB
David Bonduel ✓ Link copied!

oh ok, sorry for the misunderstanding thanks!

DM
Dilovan Matini ✓ Link copied!

Thanks for the tutorial. For the local setup, it seems, it works perfectly, but when it comes to production (Ubuntu + Nginx + HTTPS), it is not clear how configure and run it, I couldn't find any topic or video about it. I already posted the issue on the Laravel Daily channel on Discord, but nobody could help me. So if you could make a tutorial about setting up on the production using HTTPS, it will be great.

M
Modestas ✓ Link copied!

Hi, I can't seem to find your discord message. Please ping me on Discord itself with the issue.

As for setup, the whole proccess is written on the official documentation https://laravel.com/docs/11.x/reverb#production

DM
Dilovan Matini ✓ Link copied!

Thanks for your reply. I have tagged your name in Discord.

Regarding Laravel official documentation, I have checked line by line. It is more about how to setup up on Forge.

RC
Robinson Castro ✓ Link copied!

Would using Reverb always require running two workers simultaneously to handle any job and client event broadcasting separately?

M
Modestas ✓ Link copied!

Jobs are not really required for reverb, since you can dispatch events in "sync".

But in reality, they would greatly help with your application speed/load times. So yes, I would keep two separate queue workers running with two different names. One for broadcasting and another for any other job.

A
angel ✓ Link copied!

Hoo great ! So I don't need pusher or something like that to make it work ?

M
Modestas ✓ Link copied!

you do need reverb :)

We'd Love Your Feedback

Tell us what you like or what we can improve

Feel free to share anything you like or dislike about this page or the platform in general.