IBM Cloud で作った Node-RED で freee API とやり取りするメモ

IBM Cloud で作った Node-RED で freee API とやり取りするメモです。

明日の freee / IBM Cloud / LINE API アプリ開発勉強会 で登壇するにあたって、結構、いろいろと知見が貯りました。

  • IBM Cloud で作った Node-RED で freee API にアクセストークンを取得
  • 事業所一覧 API のようなシンプルなAPIにつなげる
  • 一定時間でアクセストークンを更新する(リフレッシュトークン)

このあたりの流れをインポートしてすぐ使える Node-RED のフロとともに書き残しておきます。

今回は Node-RED は 1.1.0 のバージョン、IBM Cloud と freee API は 2020/9 時点の情報で進めます。

IBM Cloud で Node-RED をつくる

まず、IBM Cloud で Node-RED をつくります。

image

このあたりの作り方は、以下の記事が参考になります。

出来上がった Node-RED にフローをインポート

フローのインポート を参考に、以下のフローJSONデータをインポートします。

[{"id":"b2532065.b21a2","type":"http request","z":"82351f6f.25399","name":"","method":"POST","ret":"txt","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","x":830,"y":340,"wires":[["d6ef6cad.18ea8"]]},{"id":"17d5b06b.374ae","type":"http in","z":"82351f6f.25399","name":"","url":"/redirect/freee","method":"get","upload":false,"swaggerDoc":"","x":150,"y":200,"wires":[["846b2044.5759f","cd822933.32bec8"]]},{"id":"846b2044.5759f","type":"http response","z":"82351f6f.25399","name":"","statusCode":"","headers":{},"x":370,"y":240,"wires":[]},{"id":"e21afae3.662b68","type":"comment","z":"82351f6f.25399","name":"freeeでリダイレクトで認可コードを受け取るURL /redirect","info":"","x":270,"y":160,"wires":[]},{"id":"db6f1f7a.627e9","type":"change","z":"82351f6f.25399","name":"認可コード記録","rules":[{"t":"set","p":"freeeAuthorizationCode","pt":"global","to":"payload.code","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":580,"y":200,"wires":[["46bda872.b2def8"]]},{"id":"1918ca41.c14dd6","type":"comment","z":"82351f6f.25399","name":"アクセストークンを要求する","info":"","x":880,"y":300,"wires":[]},{"id":"4d1ae91f.8d9f48","type":"switch","z":"82351f6f.25399","name":"not null access_token","property":"payload.access_token","propertyType":"msg","rules":[{"t":"nnull"}],"checkall":"true","repair":false,"outputs":1,"x":880,"y":420,"wires":[["7e2edba8.b4dbd4"]]},{"id":"cd822933.32bec8","type":"switch","z":"82351f6f.25399","name":"not null code","property":"payload.code","propertyType":"msg","rules":[{"t":"nnull"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":390,"y":200,"wires":[["db6f1f7a.627e9"],[]]},{"id":"d6ef6cad.18ea8","type":"json","z":"82351f6f.25399","name":"","property":"payload","action":"","pretty":false,"x":970,"y":340,"wires":[["4d1ae91f.8d9f48"]]},{"id":"179ae35.2852a1d","type":"inject","z":"82351f6f.25399","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":160,"y":100,"wires":[["8fce82b1.20894"]]},{"id":"8fce82b1.20894","type":"change","z":"82351f6f.25399","name":"ClientID / ClientSecret / RedirectURI / TokenURL","rules":[{"t":"set","p":"freeeClientID","pt":"global","to":"<ClientID>","tot":"str"},{"t":"set","p":"freeeClientSecret","pt":"global","to":"<ClientSecret>","tot":"str"},{"t":"set","p":"freeeRedirectURI","pt":"global","to":"<Node-RED URL>/redirect/freee","tot":"str"},{"t":"set","p":"freeeTokenURL","pt":"global","to":"https://accounts.secure.freee.co.jp/public_api/token","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":520,"y":100,"wires":[[]]},{"id":"25f98957.77cbc6","type":"comment","z":"82351f6f.25399","name":"起動時にアプリの値を設定","info":"","x":170,"y":60,"wires":[]},{"id":"7e2edba8.b4dbd4","type":"change","z":"82351f6f.25399","name":"データ一式記録","rules":[{"t":"set","p":"freeeAccessTokenResponsePayload","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1080,"y":420,"wires":[["cc00412f.e27c"]]},{"id":"cc00412f.e27c","type":"debug","z":"82351f6f.25399","name":"global.freeeAccessTokenResponsePayload","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"$globalContext(\"freeeAccessTokenResponsePayload\")\t","targetType":"jsonata","statusVal":"","statusType":"auto","x":1130,"y":500,"wires":[]},{"id":"b2411801.2d7628","type":"comment","z":"82351f6f.25399","name":"アクセストークンを取得出来たら記録する","info":"","x":940,"y":540,"wires":[]},{"id":"46bda872.b2def8","type":"change","z":"82351f6f.25399","name":"アクセストークン設定","rules":[{"t":"set","p":"url","pt":"msg","to":"$globalContext(\"freeeTokenURL\")\t","tot":"jsonata"},{"t":"set","p":"headers.cache-control","pt":"msg","to":"no-cache","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"{}","tot":"json"},{"t":"set","p":"payload.grant_type","pt":"msg","to":"authorization_code","tot":"str"},{"t":"set","p":"payload.redirect_uri","pt":"msg","to":"$globalContext(\"freeeRedirectURI\")","tot":"jsonata"},{"t":"set","p":"payload.client_id","pt":"msg","to":"$globalContext(\"freeeClientID\")","tot":"jsonata"},{"t":"set","p":"payload.client_secret","pt":"msg","to":"$globalContext(\"freeeClientSecret\")","tot":"jsonata"},{"t":"set","p":"payload.code","pt":"msg","to":"$globalContext(\"freeeAuthorizationCode\")","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":580,"y":260,"wires":[["b2532065.b21a2"]]},{"id":"59d8ad5a.eef5d4","type":"comment","z":"82351f6f.25399","name":"リフレッシュトークンを用いてアクセストークンを取得する","info":"","x":280,"y":360,"wires":[]},{"id":"ee2095e2.9d1e38","type":"inject","z":"82351f6f.25399","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":260,"y":400,"wires":[["ad965635.2ed438"]]},{"id":"ad965635.2ed438","type":"change","z":"82351f6f.25399","name":"リフレッシュトークン設定 ","rules":[{"t":"set","p":"url","pt":"msg","to":"$globalContext(\"freeeTokenURL\")\t","tot":"jsonata"},{"t":"set","p":"headers.cache-control","pt":"msg","to":"no-cache","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"{}","tot":"json"},{"t":"set","p":"payload.grant_type","pt":"msg","to":"refresh_token","tot":"str"},{"t":"set","p":"payload.redirect_uri","pt":"msg","to":"$globalContext(\"freeeRedirectURI\")","tot":"jsonata"},{"t":"set","p":"payload.client_id","pt":"msg","to":"$globalContext(\"freeeClientID\")","tot":"jsonata"},{"t":"set","p":"payload.client_secret","pt":"msg","to":"$globalContext(\"freeeClientSecret\")","tot":"jsonata"},{"t":"set","p":"payload.refresh_token","pt":"msg","to":"$globalContext(\"freeeAccessTokenResponsePayload\").refresh_token","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":560,"y":400,"wires":[["b2532065.b21a2"]]},{"id":"e8970b56.5b3bd8","type":"http in","z":"82351f6f.25399","name":"","url":"/redirect/check","method":"get","upload":false,"swaggerDoc":"","x":170,"y":760,"wires":[["ed700993.131588"]]},{"id":"1067489.0d102b7","type":"http response","z":"82351f6f.25399","name":"","statusCode":"","headers":{},"x":730,"y":760,"wires":[]},{"id":"eb6aa206.0be78","type":"inject","z":"82351f6f.25399","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":180,"y":640,"wires":[["63140d05.8a0814"]]},{"id":"575f5df.2d36ba4","type":"http request","z":"82351f6f.25399","name":"","method":"GET","ret":"txt","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","x":640,"y":640,"wires":[["3f18a7c8.efbed8"]]},{"id":"de7a5460.ec6f08","type":"debug","z":"82351f6f.25399","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1010,"y":640,"wires":[]},{"id":"3f18a7c8.efbed8","type":"json","z":"82351f6f.25399","name":"","property":"payload","action":"","pretty":false,"x":810,"y":640,"wires":[["de7a5460.ec6f08"]]},{"id":"63140d05.8a0814","type":"change","z":"82351f6f.25399","name":"事務所情報API送信設定","rules":[{"t":"set","p":"url","pt":"msg","to":"https://api.freee.co.jp/api/1/companies","tot":"str"},{"t":"set","p":"headers.accept","pt":"msg","to":"application/json","tot":"str"},{"t":"set","p":"headers.Authorization","pt":"msg","to":"\"Bearer \" & $globalContext(\"freeeAccessTokenResponsePayload\").access_token","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":640,"wires":[["575f5df.2d36ba4"]]},{"id":"47e45438.89bd6c","type":"comment","z":"82351f6f.25399","name":"companies GET","info":"","x":160,"y":600,"wires":[]},{"id":"ed700993.131588","type":"change","z":"82351f6f.25399","name":"「This Node-RED URL Reached!」","rules":[{"t":"set","p":"payload","pt":"msg","to":"This Node-RED URL Reached!","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":460,"y":760,"wires":[["1067489.0d102b7"]]},{"id":"fe73eea9.d159","type":"comment","z":"82351f6f.25399","name":"URLの到達チェック","info":"","x":170,"y":720,"wires":[]}]

インポートできると以下のように読み込まれます。

image

IBM Cloud の Node-RED のURLを確かめてメモしておく

ここで、IBM Cloud の Node-RED のURLをメモしておきます。

image

IBM Cloud の Node-RED のエディタを表示しているURLが https://node-red-somewhere-2020-mmdd.mybluemix.net/red/#flow/xxxxxxxxx の場合、今回メモするのは https://node-red-somewhere-2020-mmdd.mybluemix.net です。

その上で /redirect/check を加えてアクセスできるか、確かめます。

image

このように「This Node-RED URL reached!」と表示されれば、今回メモするものとしては合っています。

image

こちらの到達チェックは、インポートした部分のこのフローで行っています。

freee アプリを作成する

チュートリアル を参考に freee アプリストア で、アプリを一つ作っておきます。

image

このように作りました。

権限設定を事務所の参照だけ行う

権限設定は今回は事務所の参照だけチェックします。

image

アプリ設定の権限設定に移動して、

image

事務所の参照だけチェックしたら、下書き保存して設定を反映しておきましょう。

Client ID・Client Secretをメモする

Client ID・Client Secretも今後の設定に必要です。

image

Client ID・Client Secretをメモしておきましょう。

コールバックURLの変更

コールバックURLをデフォルトのものから変更します。

image

IBM CloudのNode-REDを確認して、リダイレクトするNode-REDのURLを設定します。

たとえば、 IBM CloudのNode-REDが https://node-red-somewhere-2020-mmdd.mybluemix.net だとすれば /redirect/freee を加えたものになります。ということで https://node-red-somewhere-2020-mmdd.mybluemix.net/redirect/freee となります。

入力して、下書き保存しておきましょう。

Node-RED に設定を反映

image

こちらの ClientID / ClientSecret / RedirectURI / TokenURL となっているノードをダブルクリックします。

image

このようにデフォルトの値が入っているので <ClientID> <ClientSecret> <Node-RED URL> 部分を以下のように置き換えます。

  • <ClientID>
    • freee アプリでメモした ClientID
  • <ClientSecret>
    • freee アプリでメモした ClientSecret
  • <Node-RED URL>
    • メモしたIBM Cloud の Node-RED のURL

ここで間違えやすいのが <Node-RED URL> で、アクセスできるか確かめた /redirect/check は加えません。IBM Cloud の Node-RED のエディタを表示しているURLが https://node-red-somewhere-2020-mmdd.mybluemix.net/red/#flow/xxxxxxxxx の場合、 https://node-red-somewhere-2020-mmdd.mybluemix.net です。 node-red-somewhere-2020-mmdd.mybluemix.net の部分は、皆さんの作った環境により変わります。

image

設定出来たら Node-RED のデプロイボタンを押して反映させましょう。

freee API のアクセストークンを取得する

image

freee API のWebアプリ認証用URLから、 Node-RED に認可コードを通知します。

image

先ほどの freee アプリの設定ページに戻って、freee API のアクセストークンを取得します。Webアプリ認証用URLに書かれているURLをコピーして、ブラウザにペーストしてアクセスします。

image

別ウィンドウでアプリ連携の開始画面が表示されるので許可を押します。

image

Node-REDのリダイレクト先にアクセスして、認可コードが取得できます。

image

そして、その流れですぐにアクセストークンを取得します。Node-REDから認可コードを問い合わせて、freee APIからアクセストークンを取得します。

image

て以下の debug ノードに到達します。

image

グローバル変数のfreeeAccessTokenResponsePayloadに記録されます。

事務所情報 API を取得してみる

image

アクセストークンを取得できたので、こちらのフローで事業所一覧 API につなぎます。

image

inject ノードのこちらのボタンをクリックしましょう。

image

無事取得されて debug ノードにデータが到達し、サイドバーのデバッグタブにAPIから帰ってきた内容が表示されます。

リフレッシュトークン機能

freee APIのアクセストークンを取得する | freee Developers Community

成功すると、レスポンスとして 下記のようにアクセストークン、リフレッシュトークンが得られます。有効期限は、アクセストークンについては24時間、リフレッシュトークンでは無期限となっています。使用する際は必要に応じてrefresh、revokeしてください。

とのことで、freee API は1日でアクセストークンの期限が切れる仕様なので、一定時間でアクセストークンを更新する(リフレッシュトークン)する必要があります。

image

今回のインポートしたフローには、リフレッシュトークン機能も実装済みです。

ただ、あえてリフレッシュトークンを定期実行はさせていません。アクセストークンまわりがちゃんと動かせて、よし使うぞ!となるまでは inject ノードを都度都度クリックする形で実験できます。

image

こちらのフローにある inject ノードのこちらのボタンをクリックしましょう。

image

リフレッシュトークン取得されて debug ノードにデータが到達し、サイドバーのデバッグタブにAPIから帰ってきた内容が表示されます。

現状ではクリックをいちいちしないとリフレッシュトークンされないので、「もうこれで使い続けて行けそうだな」と思えたら inject ノードの定期実行を以下のように設定します。

image

このように、 指定した時間間隔 で時間間隔を 12時間 で設定してデプロイしましょう。 これで、 inject ノードをいちいちクリックすることなく、12時間ごとにリフレッシュされて、アクセストークンが維持されます。

実際やってみると Node-RED で freee API のデータが実際にとれるとワクワクするし、LINE Bot や Discord など他の仕組みにつないで、どんどん育てられるので面白いですね。

ぜひ試してみてくださいー。