17
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

nemAdvent Calendar 2021

Day 16

OpenAPI 定義と Aspida で型付きの Symbol API レスポンスを取り扱う

Last updated at Posted at 2022-05-08

Symbol SDK 2.x系と3.x系

Symbol SDK は3.x系からガラッと実装が変わっています。というか作り直されています。

大まかな点で変化をまとめると、

2.x 3.x
WEB APIアクセスクラスがある WEB APIに関与しない
RxJSでストリームを扱える RxJSに依存しない
TypeScript JavaScript

(全部隅々まで見たわけではないので若干間違っているかもしれませんが)

SDK 2.xの大部分はAPIアクセスクラスを占めていたと言っても過言ではなく、その部分がごっそりなくなりました。
また、全体的な実装ボリュームも削減されたので、機能としても大幅に削減されています。
RxJSはWeb APIクラスが依存していましたが、それが無くなったので依存から無くなりました。

理由はコアデブなどに確認したわけではないので、憶測ですが、

  • 規模が大きくなりすぎていて、メンテが行き届かなくなり、必要な機能だけに絞り込んだ小さなSDKを目指している
  • 他の言語と同様のインターフェイスと使い心地を維持するため、リアクティブを止めた

という狙いがあるように思えます。

参考までに公開されているパッケージのリンクを張っておきますが、この記事においては、SDKの利用については触れません。

SDK 2.x

SDK 3.x

WEB API エンドポイント

エンドポイントの定義はここで確認できます。

そして、API定義はOpen APIフォーマットで定義されており、リポジトリはこちらです。

例えば、次のようにアクセスします。

$ curl -k https://001-joey-dual.symboltest.net:3001/node/info | jq .
{
  "version": 16777987,
  "publicKey": "AAA1922FA60DB681092CBE70A9A1BAB85745025310AE7567F95EA7FD05B3D3FC",
  "networkGenerationHashSeed": "7FCCD304802016BEBBCD342A332F91FF1F3BB5E902988B352697BE245F48E836",
  "roles": 7,
  "port": 7900,
  "networkIdentifier": 152,
  "host": "001-joey-dual.symboltest.net",
  "friendlyName": "001-joey-dual",
  "nodePublicKey": "05E5C0841720AE9DA737C184429002E917F74FF7A3BF8E11AC2862C4875B3D7E"
}

よくあるJSONを返却するWEB APIです。
ここではcurlでアクセスしてみましたが、要するにSymbolのWEB APIは単なるHTTPアクセスで扱えるということです。

API アクセスの悩み

2.x系のAPIアクセスクラスでは、リクエストやそれに関するパラメタの処理に加え、レスポンスには型を付けて(RxJSのObservalなので)ストリームに流してくれていました。

const factory = new RepositoryFactoryHttp(NODE_GATEWAY_URL)
const nodeRepo = factory.createNodeRepository()
nodeRepo.getNodeInfo()
    .subscribe((resp: NodeInfo) => console.info(resp))

> NodeInfo {
>   publicKey: 'AAA1922FA60DB681092CBE70A9A1BAB85745025310AE7567F95EA7FD05B3D3FC',
>   networkGenerationHashSeed: '7FCCD304802016BEBBCD342A332F91FF1F3BB5E902988B352697BE245F48E836',
>   port: 7900,
>   networkIdentifier: 152,
>   version: 16777987,
>   roles: [ 1, 2, 4 ],
>   host: '001-joey-dual.symboltest.net',
>   friendlyName: '001-joey-dual',
>   nodePublicKey: '05E5C0841720AE9DA737C184429002E917F74FF7A3BF8E11AC2862C4875B3D7E'
> }

しかし、3.xではそれらが無くなってしまったので、WEB APIアクセスは実装者に委ねられるようになります。

HTTPアクセスについてはnode-fetchaxiossuperagentなど、好きなライブラリを使えばいいでしょう。
でも、レスポンスオブジェクトの型はどうしましょう…。

Aspida で解決するアプローチ

自分も最近ちょっと触っただけなので、あまり語れないので、他の解説記事を確認してもらいたいんですが、簡単に言うと、WEB APIに型付きでアクセスするできるようになるライブラリです。

結論から書くと、こんな感じでAPIレスポンスを得られるようになります。

const info: NodeInfoDTO = await client.node.info.$get()
console.info(server)

> {
>   version: 16777987,
>   publicKey: 'AAA1922FA60DB681092CBE70A9A1BAB85745025310AE7567F95EA7FD05B3D3FC',
>   networkGenerationHashSeed: '7FCCD304802016BEBBCD342A332F91FF1F3BB5E902988B352697BE245F48E836',
>   roles: 7,
>   port: 7900,
>   networkIdentifier: 152,
>   host: '001-joey-dual.symboltest.net',
>   friendlyName: '001-joey-dual',
>   nodePublicKey: '05E5C0841720AE9DA737C184429002E917F74FF7A3BF8E11AC2862C4875B3D7E'
> }

めちゃくちゃいいですね…。
レスポンスだけでなく、呼び出しも型補完できるので、型の力の恩恵を存分に受けることができます。

openapi2aspida

APIの定義はOpenAPIドキュメントから生成することができます。

openapi2aspidaを使って、定義からクラスを生成します。

$ npx openapi2aspida -i openapi3.yml

Symbol APIのOpenAPI定義の修正

公式で公開されている定義ではエラーになってしまうので、動作するように自分の方で修正を施しました。
一応自分なりにOpenAPIの仕様を確認した上で修正をしましたが、そんなに真面目に定義を書いたことがないので、これが正解かまではわかりません。

こちらからcloneして、npm run buildして、openapi3.ymlを生成してください。

$ git clone -b fix https://github.com/44uk/symbol-openapi.git
$ npm install && npm run build

> symbol-openapi@1.0.4 build
> swagger-cli bundle ./spec/openapi.yml --outfile _build/openapi3.yml --type yaml

Created _build/openapi3.yml from ./spec/openapi.yml

Aspida API ソース生成

ビルドしたAPI定義を指定して、クラスを生成します。

$ npx openapi2aspida -i _build/openapi3.yml
api/$api.ts was built successfully.
api/account/$api.ts was built successfully.
.
.
api/transactions/partial/$api.ts was built successfully.
api/transactions/unconfirmed/$api.ts was built successfully.

api/にクラスが生成されます。

実装

import axios from 'axios'
import axiosClient from '@aspida/axios'
import api from './api/$api'
import { NodeInfoDTO } from './api/@types'

;(async () => {
  const client = api(
    axiosClient(axios, {
      baseURL: 'https://001-joey-dual.symboltest.net:3001',
    })
  )
  const info: NodeInfoDTO = await client.node.info.$get()
  console.info(server)
})()

> {
>   version: 16777987,
>   publicKey: 'AAA1922FA60DB681092CBE70A9A1BAB85745025310AE7567F95EA7FD05B3D3FC',
>   networkGenerationHashSeed: '7FCCD304802016BEBBCD342A332F91FF1F3BB5E902988B352697BE245F48E836',
>   roles: 7,
>   port: 7900,
>   networkIdentifier: 152,
>   host: '001-joey-dual.symboltest.net',
>   friendlyName: '001-joey-dual',
>   nodePublicKey: '05E5C0841720AE9DA737C184429002E917F74FF7A3BF8E11AC2862C4875B3D7E'
> }

おまけ(OpenAPI定義からモックサーバを立てる)

せっかくなので、モックサーバも立ててみましょう。

docker run --rm -p 3000:4010 stoplight/prism:4 mock -h 0.0.0.0 https://symbol.github.io/symbol-openapi/v1.0.4/openapi3.yml

色々とモックサーバを立てるDockerイメージが公開されていましたが、定評がありそうなstoplight/prismにしてみました。

直接URLを指定して動かすことができたので、お手軽でいいですね。

立ち上がったらhttp://localhost:3000/node/infoにアクセスしてみましょう。

symbol-bootstrapを使えば簡単にローカルにノードを立ち上げたり、プライベートネットワークを立ち上げることもできますが、かなりの容量やスペックを必要とします。

ダミーレスポンスで用が足りる場合や、開発時・テスト時に活用して見てください。

まとめ

WEB APIアクセスクラスが無くなったことで、symbol-sdk単体ではAPIからデータの取得をできなくなってしまいました。
その分だけハードルが上がってしまったかもしれませんが、RxJSの学習もなかなかに難儀でした。

むしろより一般的な知識で実装すればいいので、プログラミング初学者としてはSymbol APIを扱うために学習したことの応用が効きやすく、習熟者であれば既存の知識で実装することができるでしょう。

今回はAspidaを用いた一例の記事でしたが、OpenAPIを利用した型付与の方法は他にもあるようですし、型が必要ないならどうぞ任意のHTTPクライアントをご利用ください。

キミだけのWEB APIアクセスノウハウを構築しよう!

17
12
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
17
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?