Getting Started
rx-nostr の構造を理解するためには中心となる 3 種類の登場人物について知る必要があります。 それは RxReq
, RxNostr
とあなたのアプリケーションです。rx-nostr の世界ではこれら 3 種類の登場人物の間を RxReq -> RxNostr -> Your Application
の単方向に Packet と呼ばれるデータが流れます。実際のコードを見る前に、まずは RxReq
と RxNostr
が一体何者であるのかを確認しましょう。
RxReq
は REQ メッセージ を組み立てるために必要な情報 (ReqPacket
) を RxNostr
に送出するオブジェクトです。あなたは RxReq
が提供するインターフェースを通じて、間接的に REQ を発行することができます。ここで、RxReq
はあくまで REQ メッセージに必要な情報を提供するだけで、リレーとの交信を行うのは次に説明する RxNostr
の役目であることに注意してください。
RxNostr
は受け取った ReqPacket
をもとにリレーとの間に REQ サブスクリプションを確立し、これを管理するオブジェクトです。あなたは RxNostr
が提供するインターフェースを通じて、EVENT メッセージをはじめとしたリレーからもたらされる各種の情報を Packet として受け取ることができます。
なお、RxNostr
はひとつのリレープールと関連づいています。言い換えると、同じ RxNostr
インスタンスの上では同一のリレーとの通信はすべてひとつの WebSocket 接続にまとめあげられ、逆に、異なるインスタンスの間では同一リレーに対しても異なる WebSocket 接続が確立されるということです。
全体の流れを眺めたところで、早速最小の Nostr アプリケーションを構築してみましょう!まずは RxNostr
オブジェクトを生成して、リレープールと関連付けます。
import { createRxNostr } from "rx-nostr";
const rxNostr = createRxNostr();
rxNostr.setDefaultRelays([
"wss://relay1.example.com",
"wss://relay2.example.com",
]);
次に RxReq
オブジェクトを生成して、RxNostr
と関連付けます。これで RxReq
から RxNostr
に ReqPacket
を送出する準備が整いました。
import { createRxNostr, createRxForwardReq } from "rx-nostr";
const rxNostr = createRxNostr();
rxNostr.setDefaultRelays([
"wss://relay1.example.com",
"wss://relay2.example.com",
]);
const rxReq = createRxForwardReq();
rxNostr.use(rxReq);
rxNostr.use()
の返り値は subscribe()
可能なオブジェクトです。REQ の結果として得られる EventPacket
をここで受け取ることができます。つまり、以下のハイライト部分が RxReq -> RxNostr -> Your Application
フローにおける Your Application
相当の部分です。
import { createRxNostr, createRxForwardReq } from "rx-nostr";
const rxNostr = createRxNostr();
rxNostr.setDefaultRelays([
"wss://relay1.example.com",
"wss://relay2.example.com",
]);
const rxReq = createRxForwardReq();
rxNostr.use(rxReq).subscribe((packet) => {
// これがあなたのアプリケーションです!
console.log(packet);
});
RxJS Tips
use()
の返り値は厳密には RxJS の Observable
ですが、それについて知っておくことは必須ではありません。しかし、RxJS に親しんでいる開発者であれば RxJS の資産との連携が可能です。
しかしこのアプリケーションはまだ何も仕事をしないでしょう。なぜなら Packet が流れてこないからです。そう、ReqPacket
を送出しなければなりませんね。
import { createRxNostr, createRxForwardReq } from "rx-nostr";
const rxNostr = createRxNostr();
rxNostr.setDefaultRelays([
"wss://relay1.example.com",
"wss://relay2.example.com",
]);
const rxReq = createRxForwardReq();
rxNostr.use(rxReq).subscribe((packet) => {
// これがあなたのアプリケーションです!
console.log(packet);
});
// kind1 event を待ち受けるために REQ メッセージを発行します。
rxReq.emit({ kinds: [1] });
16, 17 行目を追加しました。さほど不思議なコードではないはずです。 これによって、RxReq
は RxNostr
に向かって ReqPacket
をひとつ送出します。RxNostr
は受け取った Packet をもとに REQ サブスクリプションを確立・購読し、購読されたイベントが 13 行目で消費されることになるでしょう。おめでとうございます!タイムラインを表示するアプリケーションの完成です!
ただ少し待ってください、最後にひと仕事だけ残っています。このままでは購読は永遠に続きます。CLOSE メッセージを送出しなければなりません。
rx-nostr では subscribe()
の結果を unsubscribe()
することによって、use()
で関連づいている REQ をすべて CLOSE することができます。少し不格好ですがここでは 10 秒後に CLOSE する、ということにしましょう。次のようにコードを追加します。
import { createRxNostr, createRxForwardReq } from "rx-nostr";
const rxNostr = createRxNostr();
rxNostr.setDefaultRelays([
"wss://relay1.example.com",
"wss://relay2.example.com",
]);
const rxReq = createRxForwardReq();
const subscription = rxNostr.use(rxReq).subscribe((packet) => {
// これがあなたのアプリケーションです!
console.log(packet);
});
// kind1 event を待ち受けるために REQ メッセージを発行します。
rxReq.emit({ kinds: [1] });
// 10 秒後に CLOSE メッセージを送信します。
setTimeout(() => {
subscription.unsubscribe();
}, 10 * 1000);
完璧です!