Introduction
MomentoでWebhook機能が使えるようになりました!
Webhookを使えば、Momento Topicsへpublishしたタイミングで
任意のエンドポイントのアクションを実行することができます。
例えば、こちらの記事では、Momento TopicsとWebhookを利用した多言語チャットのデモを紹介しています。
デモではMomento Topicsへメッセージをpublishすると、
WebhookでAWS Lambdaが起動し、各言語へ翻訳してそれぞれのTopicにpublishします。
ユーザーは希望する言語(デモ画面右上)のTopicをsubscribeしているので、
選択した言語でメッセージを見ることができます。
本稿ではngrokとTypeScriptで実装したアプリを使って、
Momento Webhook機能を確認してみます。
Momento?
Momentoはこのblogで紹介している、
クラウドネイティブな各種サーバレスサービスです。
Data Caceh,メッセージング(Topics),Vectorデータベース(Vector Index)と、
用途に応じて各種サービスを提供しています。
Environment
- MacBook Pro (13-inch, M1, 2020)
- OS : MacOS 13.5.2
- ngrok : 3.4.0
- node : v20.8.1
Setup
Momentoの設定
まずはMomentoキャッシュを作成します。
Momento Consoleにアクセスしましょう。
アカウントがまだないならここでSignupしてログインします。
ログインしたら、キャッシュ一覧からキャッシュ作成を選択し、
キャッシュ名を記述してクラウドプロバイダーとリージョンを指定します。
今回は「webhook_cache」という名前のキャッシュを作成しました。
次に、トークンのページにアクセスして、
先ほど作成したキャッシュのアクセス権限を持つ
Access Tokenを生成します。
「トークンを生成する」ボタンをクリックするとトークンが生成されるので、
環境変数に設定しておきましょう。
% export MOMENTO_TOKEN="<Auth Token>"
Webhook作成
作成したキャッシュの画面左メニューから「Webhooks」を選択します。
「Webhookの作成」ボタンを押して作成画面へいきましょう。
Webhook名は適当に決めて、トピック名は「webhook_topic」とします。
「Webhookの宛先」はあとで修正するので、適当に設定します。
Webhookを生成すると、それに対応するシークレット文字列が生成されます。
この文字列を使うと、リクエストがMomento から発信されたものかどうか検証できます。
これも覚えておきましょう。
ngrokの設定
ngrok(エングロック)はローカルで動作しているWebサーバを、
パブリックインターネット上に一時的に公開できるツールです。
例えば、自分のPCで開発しているWebアプリケーションを手軽に他の人に見せたり、
今回のようなWebhookサービスを試すときに便利です。
ngrokを起動すると公開用のURLが作成されるので、
そのURLを使用してローカル環境にアクセスできます。
このURLは、ngrokを停止するまで有効となります。
MacならHomebrewでインストールできます。
% brew install ngrok/ngrok/ngrok
あとはsignupしてauthtokenを取得して設定します。
ここなどを確認して設定しましょう。
設定できたら、↓のように起動できます。
これは、ローカルの3000番ポートを
インターネットに公開することになります。
% ngrok http 3000
起動すると下記のような情報が表示されます。
Forwardingに、実際に割り当てられたアドレスが表示されます。
ngrok (Ctrl+C to quit)
Build better APIs with ngrok. Early access: ngrok.com/early-access
Session Status online
Account xxxxxx (Plan: Free)
Version 3.4.0
Region United States (us)
Latency 881ms
Web Interface http://127.0.0.1:4040
Forwarding https://xxxxxx.ngrok-free.app -> http://localhost:3000
Connections ttl opn rt1 rt5 p50 p90
2 0 0.02 0.01 0.03 0.05
HTTP Requests
-------------
GET /favicon.ico 404 File not found
GET / 200 OK
3000番ポートをフォワードするように起動し、
割り当てられたアドレスをMomentoのWebhook画面の
Webhookの宛先に設定しましょう。
これで事前準備は完了です。
Try
では、ngrokからフォワードする先のWebアプリを実装します。
まずは必要なモジュールをインストールしておきます。
% npm install @gomomento/sdk crypto --save
% npm install typescript ts-node @types/node --save-dev
最初はexpress使おうとしたんですが、
なぜかWebhookからPOSTのbodyが受け取れなかったので
httpモジュール使ってます。
POSTのリクエストを受けると、bodyをパースして、
うけとった値(body.text)を表示してます。
また、didRequestComeFromMomentoの結果がtrueであれば、
Webhookからきたリクエストだと判断されます。
//index.ts
import http from 'http';
import crypto from 'crypto';
//Webhookからのリクエストかどうかチェックする
function didRequestComeFromMomento (req:any,rawBody:string): boolean {
const hash = crypto.createHmac("SHA3-256", "<Webhook生成時のシークレット文字列>");
const hashed = hash.update(rawBody).digest('hex');
return hashed === req.headers['momento-signature'];
}
const server = http.createServer((req, res) => {
if (req.url === '/' && req.method === 'POST') {
let body = '';
req.on('data', chunk => {
body += chunk.toString(); // raw body
});
req.on('end', () => {
console.log('Body.text:', JSON.parse(body).text);
console.log("didRequestComeFromMomento : "
+ didRequestComeFromMomento(req,body));
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'hello' }));
});
} else {
res.writeHead(404);
res.end();
}
});
const port = 3000;
server.listen(port, () => console.log(`Server is running at http://localhost:${port}`));
デモではWebhookで起動されたLambdaがメッセージを各言語に翻訳して、
各言語用のTopicsにそれぞれ対応したメッセージをpublishしてます。
作成したモジュールを起動させておきます。
% npx ts-node index.ts
Server is running at http://localhost:3000
この時点で、ngrokから割り当てられたアドレスにアクセスすると
ローカルのサーバにフォワードされます。
ではMomento Topicsにメッセージをpublishして
Webhookを起動してみましょう。
Topicsにpublushするサンプルコードは下記です。
//pub.ts
import {
TopicClient,
TopicPublish,
Configurations,
CredentialProvider,
} from '@gomomento/sdk';
const cacheName = "webhook_cache";
const topicName = "webhook_topic";
async function main() {
const momento = new TopicClient({
configuration: Configurations.Laptop.v1(),
credentialProvider: CredentialProvider.fromEnvironmentVariable({
environmentVariableName: 'MOMENTO_TOKEN',
}),
});
const message = { 'message': "bar" };
console.log(
`Publishing cacheName=${cacheName}, topicName=${topicName}, value=${message}`
);
const publishResponse = await momento.publish(
cacheName, topicName, JSON.stringify(message));
if (publishResponse instanceof TopicPublish.Success) {
console.log('Value published successfully!');
} else {
console.log(`Error publishing value: ${publishResponse.toString()}`);
}
}
main()
.then(() => {
console.log('publish success.');
})
.catch((e: Error) => {
console.error(`Uncaught exception while running example: ${e.message}`);
throw e;
});
さきほど作成したMomento Cache名とTopics名を指定してpublishしてます。
では実行してみましょう。
% npx ts-node pub.ts
WEBサーバにWebhookリクエストがきており、
ログがでているはずです。
# WEBサーバのログ
・・・
Body.text: {"message":"bar"}
"event_timestamp":1701762660904,"publish_timestamp":1701762624505,"topic_sequence_number":1,"token_id":"","text":"{\"message\":\"bar\"}"}
didRequestComeFromMomento : true
ちなみに、プログラムでwebhook_topicをsubscribeしておけば
普通にpublishされたメッセージを受け取ることができます。
Summary
今回はMomentoの新機能、Webhookを動かしてみました。
デモでは多言語翻訳チャットを実装していますが、
他にもいろいろな使い方ができそうな機能です。