最近お仕事でNode-REDとPython2.7(Flask)を使ってAPIサーバを作る事がありました。
どちらも簡単にAPIサーバが作成出来ますので、今回はそれぞれどの様に記述するとAPIサーバが出来るのか比較してみたいと思います。

[1]概要

作成するもの

今回は3つの機能を実装したいと思います。実装する機能は以下の通りです。

curlからGETメソッドを受け取って「Hello World」を返す

curlからPOSTメソッドでデータを受け取り、オウム返しをする

curlからPOSTメソッドで受け取ったデータをdashDBに追加する

事前準備

今回の比較検証をするために必要な項目を以下に纏めました。各項目の詳細については省略致しますが、もし不明な点等があればFacebookのBluemixユーザコミュニティ等に質問して頂けると良いかと思います。

事前にBluemixで作成するランタイムとサービス

  • Node-RED(ボイラープレート)
  • dashDB(サービス)※dashDB作成時に、Node-REDと接続設定をして作成して下さい
    ※Pythonについては節約のためローカルPC(Windows7 32bit)上で実行致します。

ローカル環境(Windows7 32bit)へPython2.7とFlaskを導入

pip install Flask

ローカル環境へcurlのインストール

dashDBでテーブルとカラムを作成

  • dashDBの管理コンソールにログイン頂き、「tables→Add Table」とクリック後、以下のDDLをペーストして作成して下さい。
    ※カラムの型と長さは皆様の検証要件に合わせて変更をお願い致します。
CREATE TABLE "DEV_LIST"
(
"NAME" VARCHAR(20)
);

dashDBの資格情報の取得

Flaskのプログラムで利用するため、資格情報の「dsn」の値をメモしておいて下さい。
ws000003

Flaskのプログラムについて

Pythonのソースコードを記載していきますが、基本は以下に記載したとおり「ここにプログラムを記入していきます」の中に実装する機能を作成します。
Flaskは軽量フレームワークという名の通り、基本は1つのPythonプログラムでAPIサーバを構築する事が出来ます。
Djangoとは違い複数のファイルを編集する必要が無い分、フレームワークの学習コストの削減と構築スピードのアップが見込めそうですね。

sample.py

# -*- coding: utf-8 -*-
from flask import Flask, jsonify, request, url_for, abort, Response
import os

# flaskの設定
app = Flask(__name__)
port = int(os.getenv('VCAP_APP_PORT', 8080))

#########################
##ここにプログラムを記入していきます
#########################

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=port)

比較

GETメソッドの受け取り方

○Flask
以下の「sample.py」をローカルPC上に配置して、コマンドプロンプトからプログラムを実行して下さい。
実行後、別のコマンドプロンプトを起動し、リクエストを送信すると「Hello World」が返ってきます。

・sample.py

# -*- coding: utf-8 -*-
from flask import Flask, jsonify, request, url_for, abort, Response
import os

# flaskの設定
app = Flask(__name__)
port = int(os.getenv('VCAP_APP_PORT', 8080))

@app.route("/api/v1", methods=['GET'])
  def index():
    response = jsonify({'message':'Hello World'})
    response.status_code = 200
    return response

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=port)

・実行方法

> python sample.py
* Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)

・curlでリクエストを送信
別のコマンドプロンプトを起動して、以下のコマンドを実行。

> curl -k -X GET http://127.0.0.1:8080/api/v1
{
"message": "Hello World"
}

如何でしょうか、たった16行程度でAPIサーバが出来てしまいました。
それでは、次はNode-RED側を見てみましょう。

○Node-RED
以下のソースコードをNode-REDへインポートして下さい。

[{"id":"cdf09625.42d3a8","type":"http in","z":"1d647ce0.24654b","name":"","url":"/api/v1","method":"get","swaggerDoc":"","x":134,"y":79.5,"wires":[["aeee5b49.7fe38"]]},{"id":"a2adb0d.b5d0fd","type":"http response","z":"1d647ce0.24654b","name":"","x":486,"y":79,"wires":[]},{"id":"aeee5b49.7fe38","type":"function","z":"1d647ce0.24654b","name":"レスポンス生成","func":"msg.payload = {\n \"message\": \"Hello World\"\n};\nreturn msg;","outputs":1,"noerr":0,"x":319,"y":79.5,"wires":[["a2adb0d.b5d0fd"]]}]

インポートする以下の様な3つのノードが表示されます。
ws000000

レスポンス生成と書かれたFunctionノードの中身は以下の通りです。

msg.payload = {
    "message": "Hello World"
};
return msg;

これで必要なロジックの構築は完了です。
それでは「Deploy」ボタンをクリックして頂き、コマンドプロンプトからリクエストを送信して下さい。
※FQDN:皆様の環境に合わせて変更をお願い致します

> curl -k -X GET "https://FQDN.mybluemix.net/api/v1"
{"message":"Hello World"}

如何でしょうか、Node-RED側もFlaskに負けず簡単にAPIサーバが構築出来ますね。

POSTデータの受け取り方

次はcurlからPOSTメソッドで送信されたデータを受け取り、レスポンスとして返す方法を比較したいと思います。
比較に移る前に2点補足があります。
1点目、pythonもNode-REDも基本的にJSON形式でデータを渡す事になります。そのため、curlコマンドデータをPOSTする際は「-H “Content-type: application/json”」を付与する必要がありますのでご注意下さい。
2点目、基本的にJSON形式でデータを送信する場合は、「-d {“message”:”Hellow World”}」という形にする事が一般的ですが、pythonだけ「-d “Hellow World”」といった形でも送信する事が可能です。pythonでは今回、「-d “Hellow World”」でデータを送信したいと思います

○Flask
以下の「sample.py」が、POSTされたデータを受取るためのプログラムになります。
カールコマンドから-dを指定してデータを送信した場合、-dの値が「request.data」に格納されますので、これを利用してレスポンスを返します。また、「methods=[‘XXX’]」の部分をPOSTに変更することでリクエストを受けとる事が出来ます。

sample.py

# -*- coding: utf-8 -*-
from flask import Flask, jsonify, request, url_for, abort, Response
import os

# flaskの設定
app = Flask(__name__)
port = int(os.getenv('VCAP_APP_PORT', 8080))

@app.route("/api/v1/data", methods=['POST'])
def data():
    data = request.data
    response = jsonify({'message':data})
    response.status_code = 200
    return response

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=port)

・実行方法

> python sample.py
* Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)

・curlでリクエストを送信

> curl -k -X POST -H "Content-type: application/json" -d "Hello World2" "http://127.0.0.1:8080/api/v1/data"
{
"message": "Hello World2"
}

如何でしょうか、POSTメソッドのロジックも簡単に作成出来ますね。
Flask凄い!。

○Node-RED

以下のソースコードをNode-REDへインポートして下さい。

[{"id":"a5837651.b7b0a8","type":"http in","z":"1d647ce0.24654b","name":"","url":"/api/v1/data","method":"post","swaggerDoc":"","x":145,"y":85,"wires":[["8a11f836.10bf28"]]},{"id":"8a11f836.10bf28","type":"function","z":"1d647ce0.24654b","name":"レスポンス生成2","func":"data = msg.payload.data;\nmsg.payload = {\n    \"message\": data\n};\nreturn msg;","outputs":1,"noerr":0,"x":339,"y":85,"wires":[["c4e8b312.63ca4"]]},{"id":"c4e8b312.63ca4","type":"http response","z":"1d647ce0.24654b","name":"","x":500,"y":85,"wires":[]}]

インポートすると、またまた3つのノードが表示されます。
ws000001

レスポンス生成2と書かれたFunctionノードの中身は以下の通りです。
Node-REDの場合、curlから送信されたデータは「msg.payload.XXXX」に格納されます。この「XXXX」は、curlコマンドで指定した「-d “{“XXXX”:”<何かしらのデータ>”}”」の部分にひも付きます。
今回は「curl -k -X POST -H “Content-type: application/json” -d “{\”data\”:\”Hello World2\”}” “https://FQDN.mybluemix.net/api/v1/data”」とデータを送信しますので、「msg.payload.data」とします。
※「-d “{\”test\”:\”Hello World2\”}”」と送信した場合は、「msg.payload.test」にデータが格納されます

data = msg.payload.data;
msg.payload = {
    "message": data
};
return msg;

これで必要なロジックの構築は完了です。
それでは「Deploy」ボタンをクリックして頂き、コマンドプロンプトからリクエストを送信して下さい。

> curl -k -X POST -H "Content-type: application/json" -d "{\"data\":\"Hello World2\"}" "https://FQDN.mybluemix.net/api/v1/data"
{"message":"Hello World2"}

如何でしょうか、またまたNode-RED側もFlaskに負けず簡単にAPIサーバが構築出来ますね。
Node-REDも凄い!。

POSTで受け取ったデータをdashDBにデータを追加してみる

それでは最後の集大成として、dashDBにデータを登録するAPIサーバを構築したいと思います。

○Flask
pythonでは、dashDBへ簡単に接続し、SQLを発行出来る「ibm_db」というライブラリーが配布されております。
まずはこちらのインストールから行います。以下のコマンドを実行すると「ibm_db」がインストールされます。

> pip install ibm_db

以下の「sample.py」が、POSTされたデータを受け取り、dashDBへデータを登録するためのプログラムになります。
リクエストの受け取り方は先程と変わりません。dashDBにデータを登録(INSERT)するためのクエリを変数に格納し、実行するだけで、簡単に操作が行なえます。ibm_dbの詳しい使い方についてはこちらのリンクをご確認下さい。
※DASH_PASS :dashDBの資格情報から取得できる「dsn」の情報を登録して下さい。

sample.py

# -*- coding: utf-8 -*-
from flask import Flask, jsonify, request, url_for, abort, Response
import os
import ibm_db

# flaskの設定
app = Flask(__name__)
port = int(os.getenv('VCAP_APP_PORT', 8080))

# 変数設定
DASH_PASS = '<INPUT YOUR DSN>'
conn = ibm_db.connect(DASH_PASS,"","")

@app.route("/api/v1/data/in", methods=['POST'])
def datain():
    data = request.data
    query = "INSERT INTO DEV_LIST (NAME) VALUES ('%s')" % data
    result = ibm_db.exec_immediate(conn, query)
    if result:
      response = jsonify({'message':'Success insert'})
      response.status_code = 200
      return response
    else:
      error_mes = ibm_db.stmt_errormsg()
      print(error_mes)
      response = jsonify({'message':error_mes})
      response.status_code = 500
      return response

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=port)

・実行方法

> python sample.py
* Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)

・curlでリクエストを送信
curlコマンドを使ってデータを送信します。コマンドのオプションの指定方法は先程と変わりません。

> curl -k -X POST -H "Content-type: application/json" -d "oshima" "http://127.0.0.1:8080/api/v1/data/in"
{
  "message": "Success insert"
}

・結果確認
dashDBの管理コンソールにアクセスし、「DEV_LIST」の「Browse Data」を確認するとPOSTされたデータが登録されている事が確認出来ます。
ws000004

如何でしょうか、dashDBに登録するAPIサーバのロジックも実はこんなに簡単に書けてしまいます。
Flask最高!。
それでは、Node-RED側も見ていきたいと思います。

○Node-RED
以下のソースコードをNode-REDへインポートして下さい。

[{"id":"6d1fed0a.7ed08c","type":"http in","z":"427f6336.4b40bc","name":"","url":"/api/v1/data/in","method":"post","swaggerDoc":"","x":117,"y":640.5,"wires":[["ee6376c5.ae7ea8"]]},{"id":"ee6376c5.ae7ea8","type":"dashDB in","z":"427f6336.4b40bc","service":"dashDB-80","query":"INSERT INTO DEV_LIST (NAME) VALUES (?)","params":"msg.payload.data","name":"","x":110.5,"y":694.5,"wires":[["99d6abef.bca26"]]},{"id":"99d6abef.bca26","type":"switch","z":"427f6336.4b40bc","name":"","property":"error","propertyType":"msg","rules":[{"t":"null"},{"t":"nnull"}],"checkall":"true","outputs":2,"x":252,"y":693.5,"wires":[["7da6669c.e345"],["b3322e17.f26cd"]]},{"id":"b3322e17.f26cd","type":"function","z":"427f6336.4b40bc","name":"[500]ステータスコード生成","func":"//異常終了\nmsg.payload = {\"message\":msg.error};\nmsg.statusCode=500;\nreturn msg;","outputs":1,"noerr":0,"x":444,"y":719,"wires":[["84a72a9c.0a3f3"]]},{"id":"84a72a9c.0a3f3","type":"http response","z":"427f6336.4b40bc","name":"response","x":629,"y":720,"wires":[]},{"id":"7da6669c.e345","type":"function","z":"427f6336.4b40bc","name":"[200]ステータスコード生成","func":"//正常終了\nmsg.payload = {\"message\":\"Success insert\"};\nmsg.statusCode=200;\nreturn msg;","outputs":1,"noerr":0,"x":442,"y":669,"wires":[["cfad3937.19058"]]},{"id":"cfad3937.19058","type":"http response","z":"427f6336.4b40bc","name":"response","x":627,"y":670,"wires":[]},{"id":"3e091037.6e6bc","type":"comment","z":"427f6336.4b40bc","name":"#swithはエラー判定","info":"","x":216,"y":739.5,"wires":[]}]

インポートすると、7つのノードが表示されます。
ws000005

・dashDBノード
以下の設定がされております。
ws000008

事前にNode-REDとdashDBを接続した状態でサービスを起動すると、「Service」にdashDBのサービス名が自動で表示されます。
「Query」には、dashDBに対して実行したいSQL文を記載する事が出来ます。
「Parameter Markers」は、「Query」内の「?」に対して展開したい変数を設定することが出来ます。
※複数変数を指定する事も出来ます。その場合「変数1,変数2,変数3・・・・」カンマ区切りで記載します。

ws000008
今回の場合は、クライアントからPOSTされたデータをdashDBに格納しますので、「Query」には「INSERT INTO DEV_LIST (NAME) VALUES (?)]と記載し、「Parameter Markers」にはcurlによって送信されたデータが格納されている「msg.payload.data」を指定します。
※注意点:テーブル名を指定する箇所には「?」を使って変数展開する事が出来ない仕様みたいです。なので、テーブル毎に処理を分けたい場合は、dashDBノードの前にswitchノードを使って分岐するような処理が必要です。

・switchノード
以下の設定がされております。
dashDBノードの処理で何か異常が発生すると「msg.error」にエラー情報が格納されますので、その変数のnullチェックをする事でエラーハンドリングを行っています。
ws000007

・functionノード([200]ステータスコード生成/[500]ステータスコード生成)
それぞれ以下のように記載しております。

[200]ステータスコード生成

//正常終了
msg.payload = {"message":"Success insert"};
msg.statusCode=200;
return msg;

[500]ステータスコード生成

//異常終了
msg.payload = {"message":msg.error};
msg.statusCode=500;
return msg;

これで必要なロジックの構築は完了です。
それでは「Deploy」ボタンをクリックして頂き、コマンドプロンプトからリクエストを送信して下さい。

> curl -k -X POST -H "Content-type: application/json" -d "{\"data\":\"tokida\"}" "https://FQDN.mybluemix.net/api/v1/data/in"
{"message":"Success insert"}

dashDBを確認すると、先程Flaskで挿入されたデータの後に今回追加したデータ(tokida)が表示されている事が確認できます。
ws000009

Node-RED側も、こんなに簡単に書けてしまいます。
Node-REDも最高!。

まとめ

如何だったでしょうか?。
Bluemixを利用している皆様は普段からAPIを利用する事が多いかもしれませんが、いざ自分でAPIサーバを作ろうと思うとちょっと手が出しにくい部分が有ったかもしれません。でも、PythonもNode-REDも実は簡単にAPIサーバのロジックが書けてしまう事が本記事で何となく理解頂けたら幸いです。

おまけ

Flaskで並列処理を行うための設定

実はサンプルで記載したpythonプログラムは複数のリクエストを並列で処理することが出来ません。
でも、ご安心して下さい。簡単に複数のリクエストを受け付けて並列処理出来るようにすることが出来ます。
「app.run(host=’0.0.0.0′, port=port)」に「threaded=’true’」を追加するだけで大丈夫です。
他にも「processes」を使う方法があるようです。ネット上で検索頂くとそれぞれの違いが解説されているサイトがございますので、そちらをご参照下さい。

# -*- coding: utf-8 -*-
from flask import Flask, jsonify, request, url_for, abort, Response
import os

# flaskの設定
app = Flask(__name__)
port = int(os.getenv('VCAP_APP_PORT', 8080))

#########################
##ここにプログラムを記入していきます
#########################

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=port, threaded='true')