1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【npm】symbol-websocket:SymbolブロックチェーンのWebSocketをシンプルに扱うTypeScriptライブラリ

Last updated at Posted at 2026-01-17

はじめに

今回はnpmモジュール @nemnesia/symbol-websocket の紹介です。
READMEには使い方(最新のAPI)を置いているので、この記事では「これを入れると何がラクになるのか」「どういう割り切りで作っているか」を中心に書きます。まずは雰囲気だけつかめればOK、くらいのテンションで。

npm: https://www.npmjs.com/package/@nemnesia/symbol-websocket
GitHub: https://github.com/nemnesia/symbol-tools/tree/main/packages/symbol-websocket


3行まとめ

  • SymbolノードのWebSocketを、薄いAPIでサクッと購読できる
  • 切断しても自動でつなぎ直して、購読もいい感じに戻る
  • ノード探索やアプリ側の状態管理まで“全部やる”タイプではない(そこはアプリ側で)

インストール

npm i @nemnesia/symbol-websocket
pnpm add @nemnesia/symbol-websocket
yarn add @nemnesia/symbol-websocket

最小の使用例

import { SymbolWebSocket } from "@nemnesia/symbol-websocket";

const ws = new SymbolWebSocket({
  host: "localhost",
  ssl: true,
  timeout: 5000,
});

ws.onConnect((uid) => {
  console.log("connected uid:", uid);
});

ws.on("confirmedAdded", (message) => {
  console.log("confirmedAdded:", message);
});

ws.onError((err) => {
  console.error(
    "type:",
    err.type,
    "severity:",
    err.severity,
    "message:",
    err.message,
  );
});

ws.onClose((event) => {
  console.log("closed:", event.code, event.reason);
});

// 切断(close() は disconnect() のエイリアス)
// ws.disconnect();

SymbolWebSocketはコンストラクタ生成時に接続を開始します。


これは何?(何が嬉しい?)

@nemnesia/symbol-websocketは、SymbolノードのWebSocket(/ws)に接続して、ブロックやトランザクション系のイベントをsubscribe/unsubscribeできるようにするTypeScriptライブラリです。

ログの出し方、リトライの作法、状態管理、UI反映…そういう“アプリ都合の部分”は触らずに、毎回書きがちな「接続→uid待ち→購読→再接続→購読の復元」あたりだけをまとめて肩代わりします。このへん、地味に手間なんですよね。

できること

  • SymbolノードのWebSocketイベント購読(チャネル単位/アドレス単位)
  • 購読の登録/解除(on/off
  • 自動再接続(デフォルト有効)+再接続後の購読復元
  • エラーを分類して通知(タイプ/重大度/再接続中か…など)

やらないこと

  • ノード探索(どのノードに繋ぐか決める)
  • 複数ノード冗長化(それが欲しい場合は別パッケージの方が向きます)
  • SDK的な高レベル抽象化(ブロック/Txの型変換、DB保存、UI連携など)

想定ユースケース

  • UI/サーバ/バッチで「確定Txを監視して通知したい」
  • まずはWebSocketだけ薄く扱いたい(ログや復旧はアプリ側で設計したい)
  • statusなどの失敗イベントを拾って、アプリ都合でリカバリしたい

動作環境

  • 開発言語: TypeScript(型定義あり)
  • 実行環境: Node.js>=20(Nodeで使う場合。ここは割と大事)
  • モジュール形式: ESM(import
  • ブラウザ: Reactで検証済み(バンドラ環境で動きます。isomorphic-wsはブラウザではネイティブWebSocketを使います)

README / APIへの導線


なぜ作ったか(背景)

SymbolのWebSocket監視って、見た目はシンプルなんですが、実際にアプリへ組み込もうとすると地味にやることが増えます。作り始めは「すぐ終わるでしょ」と思いがちなんですけどね。

  • 接続して最初に返ってくる uid を待つ
  • subscribe を投げる(アドレス付き/無しがある)
  • 切断時に再接続する(間隔、回数制限)
  • 再接続時に「元の購読を戻す」
  • JSONパース失敗やネットワーク系の扱いを揃える

この「毎回同じだけど、油断すると壊れやすい部分」だけを薄く切り出したのがこのパッケージです。


このライブラリがやろうとしていること

@nemnesia/symbol-websocketは、ここだけに集中しています。逆に言うと、ここ以外はやりません。

  • ✅ WebSocket接続(ws://{host}:3000/ws/wss://{host}:3001/ws
  • subscribe/unsubscribeの送信と、コールバックの管理
  • ✅ 自動再接続+購読復元
  • ✅ エラーを「アプリが判断できる形」で通知

通知の出し方、DB保存、UI状態、ノード選定みたいな“アプリ固有の話”は利用側に任せます。


設計方針

1. APIは“素直”であること

import { SymbolWebSocket } from "@nemnesia/symbol-websocket";

const ws = new SymbolWebSocket({
  host: "your-node.example.com",
  ssl: true,
  timeout: 5000,
});
  • 余計な抽象化を足さず、WebSocketの購読をそのまま触れる
  • “全部自動化”しすぎない(挙動を追いやすくしておく)

2. フレームワークを前提にしない

このモジュールを作るにあたって、個人的に一番気を付けたのが「Node.jsの組み込みモジュールに依存しない」ことです。そこに寄せてしまうとブラウザで使えなくなるので、React(ブラウザ)やReact Nativeも視野に入れて、依存関係はなるべく素直にしてあります。

  • 特定フレームワークに寄せない
  • Node.js向け(ESM)での使い方は明示しつつ、ブラウザ/React Native側でも詰まらない構成にする

3. 失敗を「静かに」しない

  • エラーを握りつぶさない
  • 利用側で分岐しやすい形(構造化エラー)で返す

チャネル一覧(SymbolChannel)

購読できるチャネルは以下です。アドレス指定できるものはws.on(channel, address, cb)が使えます。

  • block
  • finalizedBlock
  • confirmedAdded(アドレス指定可)
  • unconfirmedAdded(アドレス指定可)
  • unconfirmedRemoved(アドレス指定可)
  • partialAdded(アドレス指定可)
  • partialRemoved(アドレス指定可)
  • cosignature(アドレス指定可)
  • status(アドレス指定可)

もう少し実用寄りの例(アドレス指定+解除)

import { SymbolWebSocket } from "@nemnesia/symbol-websocket";

const address = "TB..."; // 監視したいアドレス

const ws = new SymbolWebSocket({
  host: "001-sai-dual.symboltest.net",
  ssl: true,
  timeout: 5000,
  autoReconnect: true,
});

ws.on("unconfirmedAdded", address, (msg) => {
  console.log("unconfirmedAdded for address:", msg);
});

ws.on("status", address, (msg) => {
  console.log("status for address:", msg);
});

// 監視を止める
// ws.off('unconfirmedAdded', address);
// ws.off('status', address);

// 完全に切断(コールバックもクリーンアップ)
// ws.disconnect();

エラー設計(構造化エラーが嬉しい)

onErrorで受け取れるエラーには、たとえば次のような情報が入ります。ログの出し分けや復旧方針を決めるときに、ここが効いてきます。

  • type: timeout / network / parse / connection / unknown
  • severity: recoverable(再接続する) / fatal(再接続しない)
  • reconnecting: 再接続中か
  • reconnectAttempts: 何回目の再接続か
  • host: 接続先
ws.onError((err) => {
  if (err.severity === "fatal") {
    // ここで「致命的なので止める」等、アプリ都合で判断しやすい
    console.error("fatal error:", err.type, err.message);
    return;
  }

  if (err.reconnecting) {
    console.log("reconnecting...", err.reconnectAttempts);
  }
});

注意点

  • 自動再接続はデフォルト有効(autoReconnect: falseで無効化)
  • 再接続後は、既存の購読が自動で復元されます
  • severity: 'fatal'相当になった場合は自動再接続を止めます(通知する/再起動する等はアプリ側で)
  • off()はチャネル(とアドレス)単位で、登録済みコールバックをまとめて解除する挙動です

今後やりたいこと

  • 通知JSONの型定義追加
  • 利用者フィードバックを元にしたイベントの取り回し改善
  • 周辺ツールとの組み合わせ例の拡充

※ 破壊的変更は慎重に行う予定ですが、バージョン0なので、やるときはやります。


おわりに

SymbolのWebSocket監視は「一見簡単そうだけど、運用まで考えるとやることが増える」領域なので、薄いレイヤとして@nemnesia/symbol-websocketを挟んでおくと実装がだいぶ落ち着きます。

Issue / PR は GitHub で受け付けています(気軽にどうぞ)。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?