builders.flash メールメンバー登録
1. AWS AppSync Eventsとは ?
従来、リアルタイム通信を実現するためには、WebSocket サーバーの構築・運用、コネクション状態の管理、メッセージのファンアウト実装など、多くの技術的課題がありました。AWS AppSync Events はこれらの課題を解決し、安全で高性能なサーバーレス WebSocket API を提供します。
この機能の核心は、チャネルベースの Pub/Sub モデルにあります。開発者はチャネル名前空間を定義し、その中に様々なチャネルを動的に作成できます。クライアントは特定のチャネルをサブスクライブし、サーバーサイドのコードは簡単な HTTP リクエストでイベントをパブリッシュできます。ワイルドカード機能を使えば、複数のチャネルを一度にサブスクライブすることも可能です。
AWS AppSync Events の最大の特長は、インフラ管理からの解放です。WebSocket コネクションの確立・維持、認証、スケーリングといった煩雑な作業を AWS が全て処理するため、開発者はビジネスロジックに集中できます。数人から数百万人のユーザーまで、自動的にスケールするため、トラフィック変動の心配もありません。AWS AppSync Events API はサーバーレスで、使用した分だけ支払うため、コスト効率も優れています。
2. アーキテクチャ概要
今回実装するギルドチャットシステムのアーキテクチャは以下のとおりです。
コンポーネントと処理フロー
主なコンポーネントは以下の通りです。
フロントエンド
- Webブラウザ (HTML/JavaScript) : 今回のサンプル実装では Web ブラウザベースの UI を使用します。この HTML とJavaScriptは、AWS App Runner 上の Express.js サーバーから直接配信されます。
- ゲームクライアント (Unity、Unreal Engine など) : 同じバックエンド API を利用して、ネイティブゲームクライアントからも接続可能です。
バックエンド
- AWS App Runner 上の Express.js サーバー: RESTful API による、ギルド管理、メッセージ履歴管理、JWT 認証、といった機能と、フロントエンドの配信機能を担当します。
- AWS AppSync Events: WebSocket 接続管理を含めた、リアルタイムメッセージング部分の機能を担当します。
- AWS Lambda: AppSync Events の認証処理を行うハンドラーとしての機能を担当します。
主な処理フローは以下の通りです。
認証フロー
本サンプルでは、チャンネルへの接続 (Connect) およびメッセージの送信 (Publish) には API キーによる認証を、メッセージの受信 (Subscribe) では RS256 アルゴリズムを使用した JWT ベースの認証を使用します。JWT ベース認証のフローは以下の通りです。
- App Runner上のバックエンドサーバーが認証処理の後、JWT を発行
- クライアントが JWT を Authentication ヘッダーに格納し、AppSync Events へ Subscribe 要求を送信
- Lambda 関数が JWT を検証し、Subscribe をリクエストされたチャネルにアクセスする権限があるかどうか確認
- 権限があればアクセス許可、なければアクセス拒否として応答
※ なお本記事執筆時点では、認証方式として「API キー」「Lambda 認証」「Amazon Cognito ユーザープール」「AWS Identity and Access Management (IAM) 認証」「OpenID Connect」が選択できます。本番利用においては適切な認証方式の選定・利用をご検討ください。
通信フロー
- メッセージ送信 (Publish): クライアントがメッセージを RESTful API 経由で App Runner 上のバックエンドサーバーへ送信します。バックエンドサーバーは AppSync Events に HTTP 経由でメッセージを Publish します。
- メッセージ受信 (Subscribe): クライアントが AppSync Events の Websocket に接続することで、リアルタイムにメッセージを受信します。
なお 2025 年 3 月 13 日のアップデート で、WebSocket 接続経由でのメッセージ publish も行えるようになりました。HTTP 経由の publish と比較すると、実装がシンプルになったり、publish の通信オーバーヘッドが減ることによるレイテンシー低減などの効果が見込めます。一方で、本サンプルのように API サーバーを介した HTTP 経由の publish では、高度なメッセージモデレーションやログ取得機能を持たせやすいメリットもあります。例えばゲームチャットでは、NG ワードフィルタリングをしたいので HTTP 経由とするが、ステータス同期はレイテンシー低減のため WebSocket 経由とするなど、用途によって適した方法を選択できます。
3. AWS AppSync Event API の実装
3-1. AWS AppSync Event API の作成

Event API を作成

API に名前を入力
数秒で API が作成され、イベント API、デフォルトの名前空間、API キーが自動的に生成されます。

設定内容をコピー

3-2. AWS Lambda 認証ハンドラーの実装
実際のゲーム環境では、ギルドに所属していないプレイヤーが悪意を持ってチャットに参加することを防ぐ必要があります。Lambda 認証を使うことで、ゲームバックエンドが発行した正規の JWT トークンを検証し、各プレイヤーが自分の所属ギルドのチャネルにのみアクセスできるようにセキュリティを確保できます。
また、ほとんどのゲームでは、ログイン、キャラクター情報、アイテム管理などですでに認証されたセッション情報を持っているため、その既存の認証基盤と連携したセキュリティモデルが自然です。Lambda 認証ハンドラーを用いることで連携を容易にします。
AWS Lambda コンソールにアクセス

関数を作成

関数名とランタイムを入力
- 「関数名」: 「guild-chat-auth-handler」
- ランタイム : 「Python 3.12」
※ 最新の選択できるランタイムバージョンとは異なりますが、後述の AWS CloudShell の Python とバージョンを揃える為、「Python 3.12」を選択することを強く推奨します。
他はデフォルトの設定で「関数の作成」を選択します。

AWS CloudShell にアクセス
AWS CloudShell にアクセスします。AWS CLI を使用してZIP ファイルを Lambda 関数にデプロイするにあたり、このAWS CloudShell 上のシェルでコマンド操作を行います。


Lambda 関数に必要なパッケージとソースコードををデプロイ
AWS CloudShell を使用して、サンプルプロジェクトの ZIP ファイルから Lambda 関数に必要なパッケージとソースコードををデプロイします。

認証ハンドラーの主なポイント
この認証ハンドラーの主なポイントは以下の通りです:
JWKS エンドポイント連携: バックエンドサーバー (Express.js) が提供する JWKS (JSON Web Key Set) エンドポイントから公開鍵を取得して JWT を検証します。これにより、署名鍵の管理が簡単になります。
チャネル名とギルド名の検証: トークンのペイロードに含まれる guildname と、アクセスしようとしているチャネル名が一致するかを検証します。これにより、プレイヤーは自分が所属するギルドのチャットにのみアクセスできます。
堅牢なエラーハンドリング: 無効なトークンや不正なチャネルアクセスをきちんと拒否し、セキュリティを確保します。
※ 本番利用に向けては、要件に応じた適切な JWT 検証処理の実装をご検討ください。
3-3. Lambda 認証を使用するためのチャネル名前空間の設定
作成した Lambda 関数を AppSync Events の認証ハンドラーとして設定します。
「AWS App Sync」で作成した、「GuildChatAPI」の「設定」タブから、「認可設定」にある「追加」を選択します。

認証モードを設定
「認可モード」:「AWS Lambda」
「AWS リージョン」: 「AP-NORTHEAST-1」
「関数の ARN」: 「guild-chat-auth-handler」
を選択し、「追加」を選択します。

名前空間を作成
続いて、作成した Lambda 関数をサブスクライブ認証に使用する名前空間を作成します。
「AWS App Sync」で作成した、「GuildChatAPI」の「名前空間」から、「名前空間を作成」を選択します。

名前空間の設定
「名前空間名」: 「guild」
「この名前空間にカスタム認証を追加」: チェックを入れる
「認証モードをパブリッシュ - オプション」: 「API_KEY」
「サブスクライブ認証モード - オプション」: 「AWS_LAMBDA」
を選択し、「作成」を選択します。
※ 「default」名前空間は今回は不要のため、セキュリティ考慮する場合は削除してください。残したままでも後続の手順は進められます。

4. バックエンドサーバーの実装
バックエンドサーバーは、Node.js と Express.js を使用して実装します。今回はコンテナアプリケーションを手軽にホスティングできる AWS App Runner を利用します。
このサーバーは以下の重要な機能を提供します:
- ギルドとメンバーの管理
- Web アプリケーション UI (HTML/CSS/JavaScript) の提供
- JWT 発行と JWKS エンドポイントの提供
- メッセージの AppSync Events への転送
AWS CloudShell にアクセス
AWS CloudShell にアクセスします。しばらくはこの AWS CloudShell 上のシェルでコマンド操作を行います。


4-1. AWS CloudShell でサンプルコードを準備
AWS CloudShell を使用して、サンプルプロジェクトの ZIP ファイルからサーバーをデプロイします。ZIP ファイルには、完全に機能するギルドチャットサーバーのすべてのファイル (Dockerfile、package.json、index.js など) が含まれています。
# 作業ディレクトリへ移動
cd ~
# ZIPファイルをダウンロード
curl -L https://pages.awscloud.com/rs/112-TZM-766/images/appsync-event-api-demo-guild-chat.zip -o guild-chat.zip
# ファイルを解凍
unzip guild-chat.zip
# 解凍したディレクトリに移動
cd appsync-event-api-demo-guild-chat
# ファイル内容を確認
ls -la
4-2. Amazon Elastic Container Registry (ECR) リポジトリの作成と Docker イメージのビルド・プッシュ
ECR リポジトリの作成から Docker イメージのビルド・プッシュまで、すべて CloudShell で行います。
# リージョンを設定
export AWS_REGION=ap-northeast-1
# ECR リポジトリを作成
aws ecr create-repository --repository-name guild-chat-server
# 作成されたリポジトリの URI を取得
export ECR_REPOSITORY_URI=$(aws ecr describe-repositories --repository-names guild-chat-server --query 'repositories[0].repositoryUri' --output text)
echo "リポジトリURI: $ECR_REPOSITORY_URI"
# ECR にログイン
aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ECR_REPOSITORY_URI
# Docker イメージをビルド
docker build -t guild-chat-server .
# イメージにタグを付ける
docker tag guild-chat-server:latest $ECR_REPOSITORY_URI:latest
# イメージを ECR にプッシュ
docker push $ECR_REPOSITORY_URI:latest
# イメージの URI
echo $ECR_REPOSITORY_URI:latest
4-3. JWT 認証に用いる公開鍵・秘密鍵の作成
JWT 認証には公開鍵・秘密鍵のペアが必要となるため、以下の手順にて、openssl を用いて鍵を作成します。ここで出力した各鍵の値は後の手順で利用するため控えておいてください。
# JWT トークンの署名に用いるキーペアの作成
openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in private.pem -out public.pem
# Base64 エンコード
export JWT_PRIVATE_KEY_BASE64=$(cat private.pem | base64 -w 0)
export JWT_PUBLIC_KEY_BASE64=$(cat public.pem | base64 -w 0)
# JWT_PRIVATE_KEY_BASE64 (秘密鍵)
echo $JWT_PRIVATE_KEY_BASE64
# JWT_PUBLIC_KEY_BASE64 (公開鍵)
echo $JWT_PUBLIC_KEY_BASE64
サービスの作成を選択

4-4. AWS App Runner の設定
次に、ECR イメージを使用して AWS App Runner サービスを作成します。
AWS App Runner にアクセスします。

ソース・デプロイ設定
- 「コンテナイメージの URL」:「xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/guild-chat-server:latest」※ 前章で ECR にプッシュしたイメージの URI (:latest 含む) を入力します
- 「ECR アクセスロール」:「新しいサービスロールの作成」を選択します
- 「サービスロール名」:「AppRunnerECRAccessRole-GuildChatServer」を入力
これらを入力し、「次へ」を選択します。

サービスの設定
「サービス名」: 「guild-chat-server」
「環境変数 – オプション」では、「環境変数を追加」を選択し、次の合計 5 つの環境変数を登録します。
APPSYNC_API_KEY : 前の手順で控えた「認可モード - API キー」の値
APPSYNC_HTTP_DOMAIN : 前の手順で控えた「DNS エンドポイント - HTTP」の値
APPSYNC_REALTIME_DOMAIN : 前の手順で控えた「DNS エンドポイント - リアルタイム」の値
JWT_PRIVATE_KEY_BASE64 : 前の手順で控えた秘密鍵の値 (BASE64 エンコードしたもの)
JWT_PUBLIC_KEY_BASE64 : 前の手順で控えた公開鍵の値 (BASE64 エンコードしたもの)
他はデフォルトの設定で「次へ」を選択します。
※ 本番利用に向けては、秘密鍵の値のような機微な情報の管理には AWS Secrets Manager 等の利用をご検討ください。

デプロイ
「確認および作成」の画面で入力内容を確認後、「作成とデプロイ」を選択します。数分待ち、正常にでデプロイされたことを確認してください。


登録する環境変数の解説
APPSYNC_API_KEY : バックエンドが AppSync HTTP API へパブリッシュする際や、クライアントが WebSocket API に接続する際に利用する API_KEY です。「AWS App Sync」で作成した、「GuildChatAPI」の「設定」から確認が可能で、デフォルトの有効期限が 7 日と短くなっている為、必要に応じて有効期限を延長するよう編集してください。

APPSYNC_HTTP_DOMAIN : バックエンドがAppSync HTTP APIへパブリッシュする際に利用するエンドポイントです。「AWS App Sync」で作成した、「GuildChatAPI」の「設定」から確認可能です。
APPSYNC_REALTIME_DOMAIN : クライアントがWebSocket APIに接続する際に利用するWebSocketのエンドポイントです。「AWS App Sync」で作成した、「GuildChatAPI」の「設定」から確認可能です。

JWT_PRIVATE_KEY_BASE64 : JWTトークンに署名するための秘密鍵を Base64 エンコードしたもの。
JWT_PUBLIC_KEY_BASE64 : JWTトークンの署名を検証するための公開鍵を Base64 エンコードしたもの。
4-5. Lambda 認証のための URL 設定
最後に、Lambda 認証ハンドラーが JWKS エンドポイントにアクセスできるように環境変数を設定します。
AWS Lambda の認証ハンドラー関数 (guild-chat-auth-handler)「設定」タブから「環境変数」を選択し、「編集」を選択します。

環境変数の編集
「環境変数の追加」を選択し 1 つの環境変数を追加します。
「キー」:「JWKS_URL」
「値」: 「https://xxxxxxxxxxxx.ap-northeast-1.awsapprunner.com/jwks」※ 作成した AWS App Runner が発行したデフォルトドメインに “/jwks” を加えた値を入力します
「保存」を選択します。

5. 動作確認
AWS App Runner が発行したデフォルトドメインを Webブラウザで開ききます。表示されている任意のギルドに参加し、チャットメッセージを送信してみましょう。複数のブラウザや、シークレット モードなどを利用して複数のセッションからチャットに参加することで、リアルタイムにチャットメッセージが送受信できていることを確認できます。
ログイン画面
ログイン画面 : ユーザー名とギルド名を入力してギルドに参加します。(本サンプルではユーザー名は任意です)

チャット画面
※ もし送信したメッセージが画面に反映されない場合は、画面上部に「接続済み」の表示が出ているかの確認、「Enter」キーではなく「 送信」ボタンを押下で送信、をお試しください。

6. リソースの削除
以上でサンプルチャットアプリの動作確認は完了です。不要なコスト発生を防ぐため、リソースの後片付けをしっかりやりましょう。
- AWS AppSync Event API
- 「AWS AppSync」-「API」-「GuildChatAPI」を選択し、「削除」
- AWS Lambda 関数
- 「AWS Lambda」-「guild-chat-auth-handler」の画面から、右上の「アクション」-「関数の削除」
- AWS App Runner サービス
- 「AWS App Runner」-「サービス」-「guild-chat-server」の画面から、右上の「アクション」-「削除」
- ECR リポジトリ
- 「ECR」-「リポジトリ」から「guild-chat-server」を選択し、右上の「削除」
- Amazon CloudWatch ロググループ
- 「Amazon CloudWatch」-「ロググループ」から、以下の 3 つのロググループを選択し、右上の「アクション」-「ロググループの削除」
- /aws/apprunner/guild-chat-server/xxxxxxxxxxxxxxxxxxxxxxxx/application
- /aws/apprunner/guild-chat-server/xxxxxxxxxxxxxxxxxxxxxxxx/service
- /aws/lambda/guild-chat-auth-handler
- 「Amazon CloudWatch」-「ロググループ」から、以下の 3 つのロググループを選択し、右上の「アクション」-「ロググループの削除」
7. まとめ
筆者プロフィール

大西 啓太郎(Keitaro Onishi)
アマゾン ウェブ サービス ジャパン合同会社
ゲームソリューションアーキテクト
ゲーム会社のエンジニアを経て 2022 年にアマゾンウェブサービスジャパン合同会社に入社。現在は主にゲーム業界のお客様の技術支援を担当しています。
好きな北斗神拳の奥義は北斗残悔拳。

西坂 信哉(Shinya Nishizaka)
アマゾン ウェブ サービス ジャパン合同会社
ゲームソリューションアーキテクト
SIer のインフラエンジニアを経て 2019 年にアマゾンウェブサービスジャパン合同会社に入社。現在は主にゲーム業界のお客様の技術支援を担当しています。
二児の父として育児奮闘中。ときどきアフタヌーンティーに出かけます。
Did you find what you were looking for today?
Let us know so we can improve the quality of the content on our pages