Webアプリケーションにおいて、「チャット」や「通知」といったリアルタイム機能の実装は、長らく開発者の頭を悩ませるポイントでした。
これまでは Pusher などの外部SaaSに頼るか、Socket.IO で別途Node.jsサーバーを立てるのが定石でしたが、コストやインフラ管理の手間が課題となりがちです。
そこで登場したのが、Laravel Reverb です。
Laravelエコシステムに完全に統合されたこのWebSocketサーバーを使えば、アプリケーションサーバー内で完結し、かつ非常に高速なリアルタイム通信を実現できます。
本記事では、Laravel Reverbを使用して「リアルタイムチャット」を構築する際のアーキテクチャと、バックエンド・フロントエンドの実装コードを徹底解説します。
Laravel Reverb とは?
Laravel Reverbは、Laravel公式のWebSocketサーバーです。従来のHTTP通信(リクエスト&レスポンス)とは異なり、サーバーとクライアント間で双方向の永続的な接続を確立します。
導入のメリット
- 脱・外部依存: Pusher等の外部サービスが不要になり、ランニングコストを削減できます。
- Laravelネイティブ: 認証やイベント発火がLaravel標準の Broadcasting システムと統合されており、学習コストが低いです。
- 高速・スケーラブル: PHP(ReactPHP)で記述されており軽量。Redisを経由した水平スケーリングにも標準対応しています。
システムアーキテクチャとデータフロー
実装に入る前に、各コンポーネントがどのように連携するかを整理します。
3つの主要コンポーネント
システムは主に以下の3つで構成されます。
- Laravelアプリケーション (Web)
- 通常のAPIリクエストを処理。メッセージをDBに保存し、Reverbへイベントを通知します。
- Reverbサーバー (WebSocket)
- クライアントとの接続を維持し、Laravelから受け取ったイベントを接続中のユーザーへ配信します。
- データベース
- メッセージの永続化と、ユーザー情報の管理を行います。
通信フロー(シーケンス)
チャットメッセージが送信され、相手に届くまでの流れは以下の通りです。

この「APIで保存 → イベント発火 → WebSocketで配信」という流れが、実装の核となります。
バックエンド実装 (Laravel)
それでは、具体的なコードを見ていきましょう。
1. イベントクラスの作成
メッセージ送信を通知するイベントを作成します。ポイントは ShouldBroadcastNow インターフェースを実装することです。これにより、キューワーカーを介さず即座に配信されます。
PHP
- // app/Events/Message/MessageSent.php
- namespace App\Events\Message;
- use Illuminate\Broadcasting\Channel;
- use Illuminate\Broadcasting\PrivateChannel;
- use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
- use Illuminate\Foundation\Events\Dispatchable;
- use Illuminate\Queue\SerializesModels;
- use Illuminate\Broadcasting\InteractsWithSockets;
- class MessageSent implements ShouldBroadcastNow
- {
- use Dispatchable, InteractsWithSockets, SerializesModels;
- public $message;
- public $channelId;
- public function __construct($message, $channelId)
- {
- $this->message = $message;
- $this->channelId = $channelId;
- }
- // 配信先のチャンネル定義
- public function broadcastOn(): array
- {
- // ‘channels.{id}’ というプライベートチャンネルに配信
- return [
- new PrivateChannel(‘channels.’ . $this->channelId),
- ];
- }
- // クライアント側でリッスンするイベント名
- public function broadcastAs(): string
- {
- return ‘message.sent’;
- }
- // ペイロード(配信データ)
- public function broadcastWith(): array
- {
- return [
- ‘id’ => $this->message->id,
- ‘content’ => $this->message->content,
- ‘user_id’ => $this->message->user_id,
- ‘created_at’ => $this->message->created_at,
- ];
- }
- }
2. チャンネル認証の設定
WebSocket接続時に、ユーザーがそのチャンネルにアクセス権があるかを検証します。routes/channels.php にロジックを記述します。
PHP
- // routes/channels.php
- use Illuminate\Support\Facades\Broadcast;
- use App\Models\Channel;
- Broadcast::channel(‘channels.{channelId}’, function ($user, $channelId) {
- // ユーザーがチャンネルへのアクセス権を持っているかチェック
- // (例: 中間テーブルの存在確認や、チャンネルへの所属確認など)
- return Channel::hasAccess($user->id, $channelId);
- });
3. コントローラーの実装
メッセージ保存後に broadcast() ヘルパーを呼び出すだけです。
PHP
- // app/Http/Controllers/Api/MessageController.php
- public function store(Request $request, $channelId){
- // 1. 権限チェック & バリデーション
- // …
- // 2. DBへの保存
- $message = Message::create([
- ‘channel_id’ => $channelId,
- ‘user_id’ => auth()->id(),
- ‘content’ => $request->message,
- ]);
- // 3. イベントをブロードキャスト
- // これによりReverb経由で他ユーザーに配信されます
- broadcast(new \App\Events\Message\MessageSent($message, $channelId));
- return response()->json([‘status’ => ‘success’]);
- }
フロントエンド実装 (Vue.js + Laravel Echo)
フロントエンドでは、laravel-echo と pusher-js を使用します。ReverbはPusherプロトコル互換なので、既存のライブラリがそのまま使えます。
1. Echoの初期化
JavaScript
- // resources/js/bootstrap.js
- import Echo from ‘laravel-echo’;
- import Pusher from ‘pusher-js’;
- window.Pusher = Pusher;
- // Reverbへの接続設定
- window.Echo = new Echo({
- broadcaster: ‘reverb’,
- key: import.meta.env.VITE_REVERB_APP_KEY,
- wsHost: import.meta.env.VITE_REVERB_HOST,
- wsPort: import.meta.env.VITE_REVERB_PORT,
- wssPort: import.meta.env.VITE_REVERB_PORT,
- forceTLS: import.meta.env.VITE_REVERB_SCHEME === ‘https’,
- enabledTransports: [‘ws’, ‘wss’],
- // 認証エンドポイント
- authEndpoint: ‘/api/broadcasting/auth’,
- });
2. チャンネル購読とイベントリスニング
Vueコンポーネント内で、特定のチャンネルを購読します。
JavaScript
- // resources/js/components/MessageChannel.vue
- export default {
- data() {
- return {
- channelId: 1, // 現在のチャンネルID
- messages: []
- }
- },
- mounted() {
- // チャンネルに参加
- window.Echo.private(`channels.${this.channelId}`)
- .listen(‘.message.sent’, (e) => {
- // イベントを受信した時の処理
- console.log(‘New message:’, e);
- this.messages.push(e);
- });
- },
- beforeUnmount() {
- // コンポーネント破棄時にチャンネルから退出
- window.Echo.leave(`channels.${this.channelId}`);
- },
- methods: {
- async sendMessage() {
- // 送信自体はHTTPリクエストで行う
- await axios.post(`/api/channels/${this.channelId}/messages`, {
- message: this.newMessage
- });
- // 注: 自分の画面への反映は、レスポンスで行うかWebSocketで行うか設計次第
- }
- }
- }
実運用のポイント:インフラについて
本番環境で運用する際は、Reverbサーバーを常駐プロセスとして起動させておく必要があります。
- ポート開放: WebSocket用のポート(デフォルト8080)へのアクセスを許可する必要があります。
- AWS ALB/WAFのパス設定: パスベースの制限を行っている場合、クライアントからLaravelへのパス
app/*に加え、LaravelからReverbへのパスapps/*も許可する必要があります。ここがブロックされると「接続は成功するのにメッセージが届かない」原因となるため、許可漏れがないか必ず確認してください。 - SSL対応: 本番環境では
wss://(WebSocket Secure) が必須となるため、Nginxなどのリバースプロキシで適切にSSL終端を行う構成が推奨されます。
まとめ
Laravel Reverbを採用することで、複雑になりがちなリアルタイム通信の実装が、Laravel標準の作法だけでシンプルに完結することがお分かりいただけたかと思います。
実装の要点まとめ:
- Eventクラス:
ShouldBroadcastNowを実装して即時配信。 - 認証:
channels.phpでアクセス権を厳密に管理。 - フロントエンド: Laravel Echoを使ってイベントをリッスン。
外部サービスへの依存を減らしつつ、スケーラブルなチャット機能を構築したい場合、Reverbは間違いなく最有力な選択肢です。ぜひ今回のコードを参考に、ご自身のプロジェクトへ導入してみてください。
最後まで読んでいただきありがとうございました。