まくろぐ

Azure Table Stroage を使ってみる: TableService を Promise 化して使いやすくする

更新:
作成:

PromiseTableService クラスの概要

Node.js から Azure Table Storage を操作する場合は、azure-storage パッケージの TableService クラス を使用するのですが、このクラスは残念ながら Promise 対応 対応されておらず、旧式のコールバック形式での呼び出しが強制されます。

下記の azure-table-promise パッケージが提供している PromiseTableService クラスを使用すると、TableServicePromise 化して使用することができます。

ちなみに、下記の Issue で公式パッケージの Promise 化の議論がされているのですが、TableService クラスはいまだに対応されてませんね(2020年6月現在)。

こういった対応は本家の方でサクッとやってくれれば 3rd パーティライブラリの乱立が防げるんですけどね。。。

PromiseTableService を使ってみる

まず必要なモジュールをインストールします。 azure-storage は本家 Microsoft の TableService クラスを使うためのモジュールで、azure-table-promise がそれを Promise ラップするためのモジュールです。 ここでは TypeScript を使うので、Node.js 型定義もインストールしておきます。

npm モジュールのインストール
$ npm install --save-dev @types/node
$ npm install --save azure-storage
$ npm install --save azure-table-promise

次の MyTableStorage クラスは、PromiseTableService を使って TableStroage から情報を取得するサンプルです。 コンストラクタで PromiseTableService インスタンスを生成し、getRandomMessage() メソッドで、randommessage テーブルの値をランダムに取得しています。

myTableStorage.ts
import * as azure from 'azure-storage';
import { PromiseTableService } from 'azure-table-promise';

class MyTableStorage {
  private tableService: azure.TableService;
  private promiseTableService: PromiseTableService;
  private static TABLE_RANDOMMESSAGE = 'randommessage'; // サンプルテーブル名

  constructor() {
    // Azure Storage の接続文字列を環境変数から取得
    const connectionString = process.env.AZURE_STORAGE_CONNECTION_STRING;
    if (typeof connectionString === 'undefined') {
      console.error('AZURE_STORAGE_CONNECTION_STRING is not set');
      process.exit(1);
    }
    this.tableService = new azure.TableService(connectionString);
    this.promiseTableService = new PromiseTableService(this.tableService);
  }

  /**
   * randommessage テーブルからランダムに RowKey カラムの値を取得します。
   * テーブルが空のときは空文字列を返します。
   */
  async getRandomMessage(): Promise<string> {
    try {
      const result = await this.promiseTableService.queryEntities<any>(
        MyTableStorage.TABLE_RANDOMMESSAGE, null as any, null as any);
      const size = result.entries.length;
      if (size == 0) return '';
      const index = Math.floor(Math.random() * size);
      return result.entries[index].RowKey._;
    } catch (err) {
      console.error(err);
      throw new Error(
        'queryEntities failed - ' + MyTableStorage.TABLE_RANDOMMESSAGE);
    }
  }
}

PromiseTableService が提供するメソッド名は、TableService が提供するメソッド名と同じになっているので、直感的に Promise バージョンのメソッドを使用することができます。 上記の例では、TableService.queryEntities() の代わりに PromiseTableService.queryEntities() を使用しています。 違いは、最後のパラメーターでコールバック関数を渡すのではなく、戻り値で Promise オブジェクトを受け取るという点です。 ECMAScript 2017 の async/await キーワードを使えば、上記のように同期呼び出しを行えるため、処理の流れが分かりやすくなります。

上記の MyTableStorage の使い方はこんな感じです。

async function main() {
    const table = new MyTableStorage()
    try {
        const message = await table.getRandomMessage()
        console.log(message);
    } catch (err) {
        console.error(err);
    }
}

main();

ちなみに、PromiseTableService は独自の拡張として queryEntitiesAll というメソッドも用意していて、これを使うと 1000 レコードの壁を越えて一度に大量の結果を取得することができます(通常は continuation token を使って、繰り返し queryEntities を呼び出す必要があります)。

関連記事

まくろぐ
サイトマップまくへのメッセージ