mcpField
C
chat-room

chat-room

github.com/rivet-dev開発441.2k 回インストール1 日前に更新
リポジトリ

chat-room について

RivetKit でリアルタイムチャットルームのバックエンドを構築する開発者向けのスキルです。部屋ごとに Actor を割り当て、SQLite に履歴を保存し、接続中のクライアントへイベントで新着メッセージを配信します。

主な機能

  1. 01部屋ごとの Actor 構成
  2. 02SQLite でメッセージ履歴を保存
  3. 03メッセージ ID 順の履歴取得
  4. 04新着メッセージイベントの配信
  5. 05React フロントエンドの例

活用シーン

  1. 01部屋キーごとに分離されたチャットルームの構築
  2. 02部屋のメッセージ履歴の保存と読み込み
  3. 03接続中のクライアントへのメッセージ配信

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/rivet-dev/skills --skill chat-room

インストール後、Claude Code で/chat-roomを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

BASE_SKILL.md
index.json
openapi.json
reference/actors/
access-control.md
actions.md
actor-runtime-socket.md
appearance.md
authentication.md
communicating-between-actors.md
connections.md
debugging.md
design-patterns.md
destroy.md
errors.md
events.md
fetch-and-websocket-handler.md
helper-types.md
http-api.md
input.md
inspector-tabs.md
keys.md
kv.md
lifecycle.md
limits.md
metadata.md
queues.md
reference/actors/quickstart/
backend.md
cloudflare.md
effect.md
next-js.md

詳細

チャットルーム

重要:何かを始める前に、このスキルのディレクトリにある BASE_SKILL.md を必ず読んでください。デバッグ、エラー処理、状態管理、デプロイ、プロジェクトのセットアップに関する重要なガイダンスが記載されています。これらのルールとパターンは、すべての RivetKit の作業に適用されます。以下の内容は、すでに読み、理解していることを前提としています。

動作するサンプル

参考実装が必要な場合は、以下のテンプレートにある動作するサンプルのソースコードを読んでください。

RivetKit でチャットルームのバックエンドを構築するパターン:ルーム単位のアクター、永続的なメッセージ履歴、WebSocket 接続を介したリアルタイム配信。

スターターコード

GitHub の動作するサンプルを出発点として、必要に応じて変更してください。バックエンドは単一の chatRoom アクターで、フロントエンドは @rivetkit/react を使用する React アプリです(React のクイックスタートを参照)。

トピック概要
ルームモデルルームごとに 1 つの chatRoom アクターを割り当てます。フロントエンドでは、キーのデフォルト値は general です。別のルーム名を入力すると、別のアクターに接続します。
履歴db({ onMigrate }) で SQLite の messages テーブルを作成し、ORDER BY id ASC で読み込みます。
配信sendMessage は行を挿入した後、接続中のすべてのクライアントに型付きの newMessage イベントをブロードキャストします。
IDサンプルでは扱いません。sender は通常のアクターアクションの引数です。本番環境では、ID を接続に紐付ける必要があります。

ルームごとのアクターモデル

各ルームは、キーで指定する 1 つの Rivet アクターインスタンスです。クライアントは useActor({ name: "chatRoom", key: [roomId] }) を呼び出し、そのルームのアクターを取得または作成します。これにより、次のような利点があります。

  • 分離:各ルームの履歴と接続は、そのキーの範囲に完全に限定されます。ルームの入力値を切り替えると、フックのキーが変わり、別の履歴を持つ別のアクターに接続します。
  • 単一の直列化された書き込み元:1 つのルームに対する sendMessage の呼び出しはすべて、1 つのアクターを通じて実行されるため、ロックなしでメッセージの順序が一貫します。SQLite の AUTOINCREMENT ID が正規の順序となるため、getHistory はタイムスタンプではなく id で並べ替えます。
  • 自然なスケーリング:ルームはクラスター全体に個別に分散します。負荷の高いルームが、ほかのルームの処理を遅らせることはありません。

メッセージ履歴の保存

このサンプルでは、履歴を JSON 状態ではなく、アクターの SQLite データベースに保存します。履歴の量とクエリのニーズに応じて選択してください。

方法使用する場面実装ガイダンス
SQLite(このサンプルで使用)並べ替え、上限設定、ページネーション、検索が必要な、大量または長期間保持する履歴db({ onMigrate }) で messages テーブルを作成し、パラメーター化クエリ(c.db.execute("INSERT ... VALUES (?, ?, ?)", ...))で挿入して、ORDER BY id ASC で読み込みます。履歴はアクターがスリープしても保持され、メモリに保存したい量を超えてスケールできます。
JSON 状態直近の少量の履歴。たとえば、直近の 50~100 件のメッセージアクター状態の messages 配列に追加し、送信のたびに上限を超えた分を削除します。最も簡単な方法ですが、履歴全体がメモリに保存され、クエリレイヤーもないため、件数を制限した直近の履歴を扱う用途に限られます。

ブロードキャスト配信

新しいメッセージは、型付きのイベントを通じて接続中のクライアントに届きます。

  • アクターはevents: { newMessage: event() }を宣言します。Messageは{ sender, text, timestamp }です。
  • sendMessageアクションはサーバー側でDate.now()のタイムスタンプを付けてメッセージを作成し、messagesテーブルに挿入した後、c.broadcast("newMessage", message)を呼び出し、呼び出し元にメッセージを返します。
  • 各クライアントはuseEvent("newMessage", ...)で購読し、ローカルのリストに追加します。送信者も、ほかの全員と同じブロードキャスト経路で自分のメッセージを表示するため、すべてのクライアントが同じ処理経路を使います。
  • 履歴の読み込みは接続状態に依存します。接続の準備ができたら、クライアントは一度だけgetHistory()を呼び出して過去のメッセージを表示し、その後はイベントを利用します。

ルーム全体へのメッセージにはc.broadcast(...)を使用します。DMなど、非公開または宛先ごとのペイロードを送信する場合は、個別の接続に送信してください。これは、この例に対する推奨拡張です。

タイピングインジケーターとプレゼンス(拡張)

この例では、タイピングインジケーター、プレゼンス、参加・退出処理は実装していません。コードにはcreateConnState、onConnect、onDisconnectはありません。これらが必要な場合は、一時的な接続の動作として追加してください。

  • 一時的な状態にする:ユーザー名とタイピングフラグは接続ごとの状態に保存し、SQLiteや永続化されたアクター状態には保存しないでください。プレゼンスはライブ接続から導出されるもので、接続が切れたら消える必要があります。
  • 変更時のみブロードキャストする:ポーリングや定期的な更新ではなく、ユーザーが入力を開始または停止したときにタイピングイベントを、onConnect/onDisconnectからプレゼンスイベントを送信します。
  • クライアント側で期限切れにする:接続が切れても「入力中」の表示が残らないよう、クライアント側で短いタイムアウト後にタイピングインジケーターを解除します。

ユーザーごとの受信トレイ(拡張)

オフライン配信、DM、未読件数、通知の一斉配信には、ユーザーごとにuserInbox[userId]アクターを追加します。これは、この例に対する拡張です。

  • ルームアクターはアクター間呼び出しを通じて、各メッセージをすべてのメンバーの受信トレイアクターに転送します。これにより、ルームに接続していないユーザーにもメッセージが蓄積されます。
  • 受信トレイアクターはユーザーごとの未読状態を管理し、ユーザーがオンラインになったときに、そのユーザーがどのルームにいるかに関係なく提供します。
  • DMは、ユーザーIDをソートしたペアをキーにするchatRoomか、共有履歴のセマンティクスが不要な場合は受信トレイ間の直接配信にすることで、単純なルームとして扱えます。

アクター

  • キー:chatRoom[roomId]
  • 役割:1つのチャットルームを管理します。ルームのメッセージ履歴をSQLiteデータベースに保存し、新しいメッセージを接続中のすべてのクライアントにブロードキャストします。
  • アクション
    • sendMessage
    • getHistory
  • キュー
    • なし
  • イベント
    • newMessage
  • 状態
    • SQLite
    • messagesテーブル:id(自動インクリメントの主キー)、sender、text、timestamp

ライフサイクル

sequenceDiagram
	participant A as Client A
	participant B as Client B
	participant R as chatRoom

	A->>R: connect with key [roomId]
	Note over R: every start runs onMigrate (CREATE TABLE IF NOT EXISTS messages)
	A->>R: getHistory()
	R-->>A: Message[] ordered by id
	B->>R: connect with key [roomId]
	B->>R: getHistory()
	R-->>B: Message[] ordered by id
	A->>R: sendMessage(sender, text)
	Note over R: INSERT row with server timestamp
	R-->>A: newMessage (broadcast)
	R-->>B: newMessage (broadcast)
	A->>R: disconnect
	Note over R: history stays in SQLite for the next connection

セキュリティチェックリスト

この例は意図的に最小限にしており、以下のすべてを省略しています。本番環境に導入する前に追加してください。

  • 参加前の認証:クライアントはルーム名を知っていればどのルームにも参加でき、すべての呼び出しで sender に任意のクライアント入力を指定できます。接続認証でトークンを検証し、ID を接続状態に紐付け、履歴を提供する前にルームのメンバーシップを確認してください。アクション引数として渡された送信者名を決して信頼しないでください。
  • メッセージ長の制限:この例では空のメッセージが受け入れられ、長さの上限もありません。サーバー側でトリミングし、空のテキストを拒否して、最大長を設定してください。
  • 接続ごとのレート制限:スパムやブロードキャストの増幅を防ぐため、接続ごとに sendMessage のレートを制限してください。
  • サーバー側のタイムスタンプと ID:この例ではすでに正しく実装されています。timestamp はアクション内で Date.now() から取得し、id は SQLite の AUTOINCREMENT から取得します。この実装を維持し、クライアントから送信されたタイムスタンプや ID は決して受け入れないでください。
  • 履歴の上限:getHistory は上限なしですべての行を返します。LIMIT とページネーションを追加し、長期間稼働するルームのデータが無制限に増えないよう、古い行を削除またはアーカイブしてください。
  • パラメーター化クエリ:この例ではすでに ? プレースホルダーを使って挿入しています。ユーザーが送信したテキストを SQL 文字列の補間に決して含めないでください。

リファレンスマップ

アクター
CLI
クライアント
クックブック
デプロイ
一般
セルフホスティング

コメント