はじめに
LINEやSlackなどのチャットアプリをつかっているとメッセージが来たときにその通知が来たり、新しいメッセージが先頭に並び替えられたりします。
普段から何気なくつかっているのであまり気にしていませんが、よくよく考えてみるとこちらからアクションをしていないのに情報が更新されるのは不思議な気もしてきます。
そこで、今回はその仕組みの一端を知るためにAppSyncというAWSのサービスを使ったリアルタイム更新の方法について大きく2つのパートに分けて解説します。
前半は実際にアプリを起動してその挙動を確認します。後半はその仕組みを解説します。
今回のゴール
先に今回のゴールイメージを共有します。
スマホのLINEアプリからメッセージを送ると、自作のwebアプリケーションにそのメッセージがリアルタイムで配信されるようにしています。

ゴールに到達するための前提
本記事においては以下の登場人物がでてきます。
LINE関連
- LINE公式アカウント: メッセージの送り先として使用
- LINE Messaging API: LINEのWebhookの利用のために必要
これらの設定については過去の記事で解説していますので、参考にしてください。
https://qiita.com/course_k/items/00804420e61b7514bf30
また、作成した公式アカウントにメッセージを送信するので、友だちになっておくようにしてください。
AWS関連
- API Gateway: HTTPリクエストを受け付けるサービス。Webhookの受け口として使用
- Lambda: サーバーレスのコード実行環境。Webhookを受け取ったあとの処理に使用
- AppSync: アプリとデータを接続するAPIを提供するサービス。メッセージのリアルタイム更新を担う
- GraphQL: API用のクエリ言語。AppSyncとのやり取りに使用
- DynamoDB: NoSQLのDB。データの保存先
- Amplify Gen2: 環境構築ツール。AppSync,Lambda,DynamoDB,API Gatewayを構築
- aws-amplify: AppSyncへのGraphQL通信を行うライブラリ。フロント、Lambdaで使用
これらの役割や設定については、以降で詳しく解説します。
開発環境
本記事は以下が導入されていることを前提としており、これらの手順については触れません。
- AWSアカウント
- Git
- Node.js 22以上
- AWS CLI
また、OSはmacOS Tahoe 26.5.2です。
Windowsなどの方は適宜読み替えてください。
作業前の準備
IAMユーザーの作成
Amplifyで使用するためのIAMユーザーを作成しておきます。
名前は任意で構いません。
許可については直接アタッチでAmplifyBackendDeployFullAccessを設定します。

次に作成したIAMユーザーのアクセスキーを作成しましょう。
アクセスキーID、シークレットアクセスキーを控えておきます。
AWSプロファイルの設定
作成したアクセスキーを使って、プロファイルを設定します。
aws configure --profile <profile>
profileは任意ですが、以降の処理でAWSプロファイルを指定する際に参照するので控えておきます。
本記事ではわかりやすいようにIAMユーザー名をプロファイル名として使用します。
aws configure --profile amplify-dev
コマンドを実行すると、以下のように対話形式で情報を聞かれるので、先ほど控えたアクセスキーID、シークレットアクセスキーを使用します。

リージョンは任意のリージョンを設定します。本記事ではap-northeast-1としています。
output formatは空で構いません。
サンプルコードの用意
本記事で使用しているソースコードは以下のリポジトリに置いています。
任意のフォルダにcloneしておくことで、以降の手順に沿って実際に開発を行うことができます。
git clone https://github.com/course-k/realtime-chat-demo.git
cd realtime-chat-demo
npm install
以降のコマンドはrealtime-chat-demoディレクトリで実行します。
アプリを動かす
シークレットの設定
LINEのチャネルシークレットやチャネルアクセストークンなどの秘匿性の高い情報は、Amplifyの機能でシークレットとして設定します。
通常の環境変数のようにコードに直接値を書かないので安全です。
npx ampx sandbox secret set <secret-name> --identifier <identifier> --profile <profile>
identifierはサンドボックス(開発用環境)を区別する名前です。省略した場合はOSのユーザー名が使用されます。
profileは先ほど作成したAWSプロファイル名を指定します。
まずはLINEチャネルシークレットを設定します。
なお、LINEチャネルシークレットはLINE Developersコンソールの「チャネル基本設定」タブ、LINEチャネルアクセストークンは「Messaging API設定」タブの「チャネルアクセストークン(長期)」で発行できます。
npx ampx sandbox secret set LINE_CHANNEL_SECRET --identifier demo --profile amplify-dev
コマンドを実行すると値の入力を求められるので、チャネルシークレットを入力します。

入力内容はなにも出力されませんが、ペーストも可能です。
次にLINEチャネルアクセストークンを同じように設定します。
npx ampx sandbox secret set LINE_CHANNEL_ACCESS_TOKEN --identifier demo --profile amplify-dev
バックエンドのデプロイ
Amplifyの機能を使うことで、バックエンドのデプロイを簡単に実行することができます。
npx ampx sandbox --identifier <identifier> --profile <profile>
先ほどのシークレット設定で指定したidentifier、profileでコマンドを実行します。
npx ampx sandbox --identifier demo --profile amplify-dev
バックエンドのデプロイが始まるので、しばらくそのまま待ちます。

[Sandbox] Watching for file changes...が出力されれば起動完了です。
このターミナルは閉じずにこのまま置いておきます。

amplify_outputs.jsonというファイルがプロジェクトのルートに作成されているので、そのなかのcustom.lineWebhookUrlをコピーして、LINE Developersコンソール上のWebhookURLとして設定します。
設定後、「検証」を押下し、「成功」のモーダルが表示されれば完了です。
「Webhookの利用」がONになっていることを確認しておいてください。
アプリの起動
別のターミナルを開きます。realtime-chat-demoのディレクトリにいることを確認して、以下のコマンドを実行します。
npm run dev
localhost:5173でアプリが立ち上がるので確認します。
設定した公式アカウントにメッセージを送ると、以下のようにメッセージがリアルタイムで届き、画面に描画されます。

ここまででリアルタイム更新の実装は完了です。
これで終わりとしてもよいのですが、実際にこれらがどのような仕組みで動いているのかについて以降で解説をしていきます。
確認が終わったら
npx ampx sandbox delete --identifier demo --profile amplify-dev
でリソースを削除しておくようにしてください。
リアルタイム更新の仕組み
全体の流れの確認
今回はバックエンドの構築についてはほとんどAmplifyがやりました。また、フロントのコードについても自分で実装せずに既にあるものを使用したので、誰が何をしているかというところが不明瞭です。
まずは簡単に全体の流れを確認した後で、それぞれの役割について確認していきます。
メッセージ受信からリアルタイム表示までの流れ
全体の処理の肝になるのがAppSyncの挙動です。
処理を細かく読み解きながら、どのようにリアルタイム更新が実現されているかを確認していきます。
Webhookの受信
個人のLINEから公式アカウントに向けてメッセージを送信すると、LINEプラットフォームから、設定したWebhookサーバーに対してPOSTリクエストが飛びます。本記事ではAPI Gatewayに対してPOSTリクエストが飛びます。
これは処理を実行するLambdaが単体では外部に公開可能なエンドポイントURLを持っておらず、API Gatewayを経由してLambdaを起動するためです。
メッセージの登録(Mutation)
API Gatewayを経由して起動されたLambdaは、直接DynamoDBを操作してデータを保存するのではなく、AppSyncにデータの登録をリクエストします。
このリクエストを記述するための言語がGraphQLです。
GraphQLで記述できるリクエストには
- データの取得(Query)
- データの変更(Mutation)
- イベントの購読(Subscription)
の3種類があり、ここではcreateMessageという名前のMutationをリクエストしています。
これがリアルタイム更新の肝の1つめになります。
メッセージの保存
AppSyncはLambdaからのMutationを受け取ると、そのMutation(createMessage)に紐づく処理を実行します。ここではDynamoDBに対して、渡された値を書き込みます。
この処理はリゾルバと呼ばれ、データモデル定義からAmplifyが自動で生成しています。
※基本のCRUD操作から外れる場合などは自動生成の対象外
メッセージの購読 / 配信
アプリケーションを開くと、フロントからAppSyncに対してメッセージの購読 (Subscription) をリクエストします。このときにもGraphQLが使われます。
ここではonCreateMessageという名前のSubscriptionをリクエストしており、これはcreateMessageに紐づいています。この対応付けもAmplifyが自動で生成しています。
通常、サーバー側でデータが増えたことをフロントが知るには、定期的に問い合わせ直す(ポーリング)しかありません。
しかし、先に見たとおりLambdaはDynamoDBへ直接書き込まず、AppSyncを経由してデータを登録しています。
この経路のおかげで、変更がMutationとしてAppSyncを通るため、購読しておいた変更をAppSyncがフロントへ配信できます。
これがリアルタイム更新にAppSyncを使う理由であり、肝の2つめになります。
なお、アプリを閉じている時に届いたメッセージについては購読していないため配信されませんが、Mutationは走るのでDBにデータは保存されます。
初期表示時にはlistMessagesという別のQueryでその一覧を取得しています。
※サンプルコードではobserveQueryというラッパーを使っています。これは内部で初回の一覧取得(listMessagesに相当)と、以降のonCreateMessageなどの購読をまとめて行うものです。
まとめ
AppSyncを使うことで、LINEのようなリアルタイム更新を実現できることを確認しました。
ポイントとしては2つ
- データの更新をAppSync経由で行う
- AppSyncに対してイベント(ここではデータの更新)の購読を行う
これらによりリアルタイムでのデータを受け取ることができるようになります。
実際の本番ではもっと考慮することはいろいろあるのですが、手元で簡単に確認するレベルであればAmplifyの力も借りて、サクッと構築することができました。
AppSyncの周りはでてくるサービスが多く、どのサービスが何を担っているのか混乱することが多かったので、同じような人にとって理解の一助となっていれば幸いです。
AppSyncが実現している購読と配信の仕組みの裏側にはWebSocketというものがあったりするのですが、それについてはまた機会を見つけてまとめたいと思っています。
もしよければ、記事にいいねやコメント、フォローをいただけると大変嬉しいです。
最後までご覧いただきありがとうございました。


