まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

実を言うと前回このブログエントリを書いたのは、今日のエントリへの前フリでした:もともと以下で紹介する Swagger Editor を使う様子を紹介したかったのですが、そのためには扱う題材となる API が必要でした。というわけで簡単な仕様で、かつ試験的にクロスオリジン問題を解決することのできる(←ここ大事!)REST API を Node-RED で作ってみる、というのが上記の内容でした。同様に自分で管理可能な API があればそれを使って読み替えていただいてもいいのですが、そうでない場合まずは上記エントリを参照して、この後の紹介で管理する REST API を作っておいてください。


さて本題は「REST API のドキュメントをどのように用意するか」です。REST API の仕様書として最低限必要な情報は HTTP メソッドと URL のパス、実行時に与えるパラメータ、結果、そしてその API が何をするものかをざっと紹介したものでしょうか?気が利いているものだと各パラメータ毎の必須/オプションの情報、エラー時の情報などがあったりしてより便利になります。上記の Node-RED で作った API の場合だと対象の API は1つでこんな感じでしょうか:
メソッドパスパラメータの指定方法パラメータとその意味実行例結果の Content-Type結果
GET/getDateURLパラメータtimestamp : タイムスタンプ値(オプション、未指定時は現在時刻)curl -XGET 'http://xxx.com/getDate?timestamp=1000'text/plain指定した日付時刻の文字列


このドキュメントでもある程度は理解できると思いますが、もう少し便利に、というか、Swagger という標準フォーマットを使ってドキュメントを作ってみます。その際に便利なのが Swagger Editor です。Swagger Editor はここにアクセスしてオンライン版をそのまま使うこともできますし、Docker イメージやソフトウェアをダウンロードして専用環境下で利用することもできます。今回はオンライン版を使って紹介します。

Swagger Editor を開いて、API ドキュメントを yaml と呼ばれるテキストフォーマットで書いていきます。今回の例だとこんな感じでしょうか:
swagger: "2.0"
info:
  description: "俺の API"
  version: "0.0.1"
  title: "俺の API"
host: "**********.mybluemix.net"
basePath: "/"
tags:
- name: "myapi"
  description: "俺のAPI"
schemes:
- "http"
- "https"
paths:
  /getDate:
    get:
      tags:
      - "myapi"
      summary: "時刻を文字列で取得する"
      description: "現在時刻または指定したタイムスタンプ値の日付文字列を返す"
      produces:
      - "text/plain"
      parameters:
      - in: "query"
        name: "timestamp"
type: "number"
format: "integer" description: "タイムスタンプ値" responses: 200: description: "成功"

この内容を Swagger Editor の画面左に入力します。するとこの Swagger で定義した Open API ドキュメントが画面右に表示されます:
2017091001


今回定義したのは GET /getDate という1つの API です。ここをクリックすると展開され、パラメータなどより詳しい情報が表示されます。API ドキュメントとしても便利ですが、この Swagger によるドキュメント最大の特徴は「実行できる」ことです。このページから AJAX によるリクエストを発行して API を実行します。一般的には AJAX でリモートサイトにアクセスするにはクロスドメイン問題を考慮する必要がありますが、今回用意した API は(そのための目的もあって)クロスドメインからも実行できるような HTTP レスポンスヘッダを設定しています。というわけで実行してみましょう。"Try it out" と書かれたボタンをクリックします:
2017090902


するとパラメータ部分が入力可能な状態になります。今回は timestamp という必須ではないパラメータを指定できるようにしていることがわかります。とりあえずはここには何も設定せずにそのまま実行してみます:
2017090903


実行するには "Execute" と書かれたボタンをクリックします。すると実行した時の curl コマンドなどと一緒に実行結果も表示されます。この API はパラメータなしで実行すると現在時刻のテキストを返すようになっており、実際に実行したタイミングの日付時刻が表示されます:
2017090904


次にパラメータを指定した上で実行してみます。timestamp に 1000 と入力してみました。これはタイムスタンプの値が 1000 になる日時(1970年1月1日午前零時から1000ミリ秒後)を指定したことになります:
2017090905


この状態で "Execute" ボタンをクリックすると、実行結果は 1970/01/01 00:00:01 になるはずです。API が正しく動いていることがドキュメントの中から実行して確認することができました(クロスドメイン問題を抱えたままだとここでの実行は失敗します):
2017090906


と、これが Swagger で作った API ドキュメントの魅力です。 最後にここで定義した Swagger ドキュメントをエクスポートして(Swagger Editor 上ではない)別のサーバー上でも動くようにしてみます。今回は Node.js 上で実行できるような形式でエクスポートしてみましょう(サーバーサイド JavaScript である Node.js 上で実行する場合はクロスドメインは意識する必要がありません)。画面メニューの "Generate Server" から "nodejs-server" を選択します。すると自動的にダウンロードが始まり、nodejs-server-server-generate.zip というファイルがダウンロードされます:
2017090907


この zip ファイルを Node.js がインストールされたシステム上に展開し、"npm install" 後に "npm run start" すると起動します:
$ cd nodejs-server-server
$ npm install
$ npm run start

> api@0.0.1 prestart /home/pi/src/nodejs-server-server
> npm install

up to date in 5.819s

> api@0.0.1 start /home/pi/src/nodejs-server-server
> node index.js

Your server is listening on port 8080 (http://localhost:8080)
Swagger-ui is available on http://localhost:8080/docs

起動したドキュメントページ(上記例だと http://localhost:8080/docs)にアクセスすると Swagger Editor の右ペインでプレビューとして見ていた部分が表示されます:
2017091002


実際にパラメータを指定するなどして実行することも可能です:
2017091003


以上、自分で作った API に対する実際に動かせる Swagger ドキュメントを作り、そのドキュメントを自分のサーバー上で動かす、という一連の手順を紹介しました。開発者としては API の仕様書を読み解くだけでなく、実際に動かして返ってくる値やそのフォーマットを確認しながら利用できるので、自分が API を使って作ったアプリがうまく動かなかった時の問題切り分けにも使えるツールになるので非常に便利です。

最近メインの業務が変わって、ずっと勉強モードだったこともあってブログの更新が滞ってました。やっと心に余裕がでてきたので、しばらくシリーズで(比較的簡単な)エントリを続けてみようと思います。

というわけで、今回紹介するのは「Node-RED で REST API を作る」というものです。Node-RED をある程度使っている人ならば「何それ簡単じゃん」と思う程度のことですが、今後引き続いて紹介しようと思ってる内容の導入部分として読んでいただければと。

まず最近話題(になってると感じる)の Node-RED はビジュアルフローエディタです:
2017090801


「ノード」と呼ばれる組み合わせ可能なブロックを順番に配置/接続して、データのフローを定義し、実行します。一通りの作業をブラウザ上の GUI で行うことができる手軽さとわかりやすさが素晴らしいと思っています。Node-RED はラズベリーパイの標準 OS である Raspbian OS にも標準搭載されていますが、IBM Bluemix 環境を使ってすぐにクラウド上の環境を用意することもできます(以下は Bluemix 環境前提で紹介します):
2017090802


Node-RED は基本的に GUI でデータのフローを定義するものであって、ここで作ったものの動きをそのまま視覚化できるわけではありません。ただノードの中にはデータを受け取って HTML ベースのダッシュボードを作るようなものがあったり、HTTP のリクエスト/レスポンスのノードも用意されているので、データを HTML で出力する(そしてそれをブラウザで表示する)といった応用もすぐにできます。今回はこの HTTP リクエスト/レスポンスノードを使って REST API を作ってみます。

Node-RED 画面左のキャンバス部分から、ノードパレットから HTTP リクエストfunctionHTTP レスポンスノードを1つずつドラッグ&ドロップでキャンバスに配置します。この時点でこのような画面になります:
2017090803


これら3つのノードの左右の端をドラッグ&ドロップして以下のように接続します。Node-RED ではデータは左から右に向かって流れていきます。つまりこの場合だと「HTTP リクエスト → function → HTTP レスポンス」という順にデータが送られていくようなフローができました:
2017090804


配置した3つのノードそれぞれの属性を(ダブルクリックして)変更してみましょう。まずは HTTP リクエストノードです。ここは「どのような HTTP リクエストに対して反応するか」を属性で指定します。今回は GET /getDate という日付時刻を返す REST API を作ってみましょう。メソッドは GET 、そして URL には /getDate を指定します。これでこのサーバー上の /getDate に対して GET リクエストが送られてきた場合にこのノードから始まる一連のフローの実行が開始するようになります。編集が終わったら右上の「完了」ボタンをクリックします:
2017090805



なお GET リクエストの場合、URL パラメータで指定された値は msg.req.query 内に格納されます。例えば /getDate?a=x&b=y というリクエストを処理した場合には msg.req.query.a = x, msg.req.query.b = y という値が格納された状態で次のノードが実行されます(といったような情報が画面右の「情報」タブに表示されます。キャンバスで選択中のノードに関する説明はここから参照できます):
2017090801


その次のノードである function ノードは JavaScript を記述することができます。ある意味でビジュアルフローっぽくないというか、一般的なプログラミングに近い処理を行うノードです。今回はこのノードを使って API として返す値(今回の例であれば日付時刻)を生成します。以下のような内容に書き換えます(緑文字部分は処理に関係のないコメントなので不要です):
var dt = new Date();  //. 現在時刻
if( msg.req.query.timestamp ){
  //. timestamp パラメータが指定されていた場合は、そのタイムスタンプの時刻に設定する
  dt.setTime( msg.req.query.timestamp );
}
msg.payload = dt;   //. ペイロードに時刻文字列を設定
return msg;

2017090806




今回作成する API は時刻の文字列を返すようにしました。特にパラメータ指定なしで実行した場合は現在時刻を、timestamp というパラメータが指定された場合はその値をタイムスタンプ値(1970年1月1日午前0時からの経過ミリ秒)と見なして、その日時の時刻を返すようにしています。


そして最後に HTTP レスポンスノードの属性を設定します。普通に動かすだけであれば何も設定しなくても(このままでも)動きます。が、今回は後でこの API を外部の JavaScript からも呼び出せるような設定を加えます(外部からの呼び出しを許可するため、HTTP レスポンスヘッダにクロスオリジンを許可する項目を加えます。詳しくはこちらを参照)。HTTP レスポンスノードのヘッダに Access-Control-Allow-Origin という項目を追加し、その値を * に指定します:
2017090807


これでこの HTTP リクエストは、外部のサーバーから AJAX などで呼び出しても利用できるようになりました。


こうして Node-RED 上に作成したフローを画面右上のボタンから「デプロイ」します:
2017090808


デプロイが成功することを確認します:
2017090802


デプロイが成功したら、ウェブブラウザで http(s)://(Node-RED の動いているサーバー)/getDate にアクセスしてみます。画面に現在時刻が表示されれば成功です(実際には Node-RED が稼働している環境の言語設定などに依存するので日本時間で表示されたり、外国の時間で表示されたりしますが、同じユニバーサル時刻が表示されるはずです。下図では GMT 時間で表示されています):
2017090803


今度は timestamp パラメータを指定して、 http(s)://(Node-RED の動いているサーバー)/getDate?timestamp=1 にアクセスしてみます。これは 1970/01/01 00:00:00 から1ミリ秒経過したタイミングを指定しているので、このような表示になるはずです:
2017090804


と、まずは単純な GET アクセスだけの API を1つだけ作ってみました。Node-RED を使うと最小で3つのノードを組み合わせるだけで簡単に作れることが分かりました。同様にして POST メソッドに対応させたりすることもできますので、興味ある方はチャレンジしてみてください。


なお、Node-RED に関しては最近たて続けに日本語書籍が発表されています。Node-RED そのものに関する情報はこれらの書籍も参考にしてください(↓アマゾンへのリンクです):




 

最近、業務でブロックチェーンを使う機会が増えてきました。というか、提案段階のものも含めて大半の案件にキーワードとして出て来るようになっています(イノベーティブなアプリケーション開発に関わる部門に所属していることも関係しているとは思います)。ブロックチェーンを API 経由で使うだけでなく、その API を作ったり、データのモデリングをしたりする機会も出てきました。

まだまだ勉強段階ではありますが、実際にお客様と会話している中でも作る前から「これはブロックチェーンに向いてるなあ/向かないなあ・・・」と感じることができるようになってきました。今日のエントリはそういう話です。

ブロックチェーンは革新的な技術で注目されている一方で、まだわかりにくい部分もあり、誤解を受けている部分もあります。典型的なパターンが「現在動いている○○システムのデータベース部分をブロックチェーンに置き換えて信頼性を向上させる」というものです。まさにブロックチェーンの強みをいかしたシステム改変のようにも聞こえますが、実現に向けては落とし穴も見受けられます。


ブロックチェーンは「分散台帳」と呼ばれる改竄の難しい仕組みでデータを格納します。その意味ではブロックチェーンはデータベースであるとみなすことはできますし、その中に格納されている情報の信頼性は非常に高いと言えます。ここがブロックチェーンの従来のデータベースに対する強みであり、こういった目的で使うにはブロックチェーンは向いていると考えます。

ただ一方で、従来のデータベース(ここではリレーショナルデータベースとしましょう)にもブロックチェーンと比較した時のメリットや得意分野があります。例えば SQL のような標準化されたクエリー言語による検索はリレーショナルデータベースの特徴であり、ブロックチェーンがこの機能を持っているわけではありません(例えばブロックチェーンの実装の1つである Hyperledger Fabric の場合、SQL ライクなクエリーを定義することはできますが、SQL とは異なるものです)。またトランザクション処理のパフォーマンスはまだまだリレーショナルデータベースの方が上と言わざるを得ません。そのような状況であるにも関わらず、興味以上の理由で「今使っているシステムのデータベースをブロックチェーンに単純に置き換える」ことは必ずしも正しくならない可能性があります。


・・・と、ここまでは「ブロックチェーン」を「NoSQL データベース」に置き換えても同じ話なので、NoSQL データベースが出現した頃にも同様の議論があり、特別目新しいことではないかもしれません。ただブロックチェーンの場合はブロックチェーン特有の事情を考慮する必要もあります。

リレーショナルデータベースと NoSQL データベース(やらメモリキャッシュやら検索エンジンやら・・)を比較する場合は、それぞれのデータベースの得意・不得意を見極めた上で適材適所に配置することが理想回答になると思います。例えば商品のコマースサイトであればこんな感じでしょうか?
2017083101

↑商品マスターと、その商品を分類するためのカテゴリーマスターが存在しているものとします。商品マスターと比べてカテゴリーマスターの情報は変更頻度が少ないため、アプリケーション実行時にメモリキャッシュに一括してロードし、実行時は高速なメモリアクセスだけで参照できるような設計にします。また商品情報は柔軟かつ高速な検索ができるよう、検索サーバーを用意します(定期的に情報を更新します)。またこのアプリケーションを使って発生した取引の情報も管理するものとします。

このシステムを一般的なデータベースを使って作る場合は↓このようになります。マスターをデータベースで持ち、商品情報の一部は検索サーバーに同期します。また取引が発生した際のログもデータベースに保存します。比較的一般的で、特別に珍しい点はないと思っています:
2017083102


さて、上記の構成を持ったシステムが現在動いているものとします。この現状のシステムに対して、例えば「商品情報や取引記録の正当性を保証する目的でブロックチェーン対応する」ことを考えてみます。上述のようにブロックチェーンには向き・不向きがあるのですが、例えば現状データベースで管理している「商品情報をブロックチェーン化する」ケースを考えてみます:
2017083103


↑単純に考えるとこんな感じです。データベースで管理していた商品マスターの情報をブロックチェーンに格納するというケースです。もちろんアプリケーションの書き換えは必要ですが、システム構成自体には問題なさそうに見え、商品マスターの情報がよりセキュアに守られるように見えます。

しかし実は問題点をはらんでいます。上述のように、今回のシステムでは商品情報は検索目的で検索サーバーと同期しています。端的にいうと「商品情報は2重管理されている」想定になります。商品マスターをブロックチェーン化して改竄を困難にしたつもりが、検索サーバーが管理している商品情報については何の対策もしていないことになるので、ブロックチェーンで管理しているケースと比較すると、検索サーバー側に弱点(というか盲点)が存在していることになってしまいます:
2017090100


一方でこのシステムを使って生成される取引情報をブロックチェーン管理にする、という部分はこの取引情報の改竄は非常に困難になるため、上述のような情報の正当性を保証する目的においては(トランザクション量などの考慮を除けば)「ブロックチェーン向き」と言える改良といえます:
2017083104


ただこれについても上述のように情報のクエリーなどデータベースが得意とする機能が重要視されるケースもあるでしょう。したがってブロックチェーンがフィットするかどうかという意味では単純にデータベースをブロックチェーンに置き換える、という考え方は危険で、「何を目的として、何をブロックチェーンで管理するのか」、「データベースの方が適している要素を無視していないか」という観点でシステムを設計する必要があります。


このページのトップヘ