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 をつくります。
このあたりの作り方は、以下の記事が参考になります。
- IBM Cloud で Node-RED セットアップ (2020年9月)
- IBM Champion 柿本さんによる出来立てほやほやのセットアップ記事
- IBM CloudでNode-REDの立ち上げ方
- IBM Developer Advocate 西戸さんのセットアップ記事
出来上がった 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":[]}]
インポートできると以下のように読み込まれます。
IBM Cloud の Node-RED のURLを確かめてメモしておく
ここで、IBM Cloud の Node-RED のURLをメモしておきます。
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
を加えてアクセスできるか、確かめます。
このように「This Node-RED URL reached!」と表示されれば、今回メモするものとしては合っています。
こちらの到達チェックは、インポートした部分のこのフローで行っています。
freee アプリを作成する
チュートリアル を参考に freee アプリストア で、アプリを一つ作っておきます。
このように作りました。
権限設定を事務所の参照だけ行う
権限設定は今回は事務所の参照だけチェックします。
アプリ設定の権限設定に移動して、
事務所の参照だけチェックしたら、下書き保存して設定を反映しておきましょう。
Client ID・Client Secretをメモする
Client ID・Client Secretも今後の設定に必要です。
Client ID・Client Secretをメモしておきましょう。
コールバックURLの変更
コールバックURLをデフォルトのものから変更します。
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 に設定を反映
こちらの ClientID / ClientSecret / RedirectURI / TokenURL
となっているノードをダブルクリックします。
このようにデフォルトの値が入っているので <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
の部分は、皆さんの作った環境により変わります。
設定出来たら Node-RED のデプロイボタンを押して反映させましょう。
freee API のアクセストークンを取得する
freee API のWebアプリ認証用URLから、 Node-RED に認可コードを通知します。
先ほどの freee アプリの設定ページに戻って、freee API のアクセストークンを取得します。Webアプリ認証用URLに書かれているURLをコピーして、ブラウザにペーストしてアクセスします。
別ウィンドウでアプリ連携の開始画面が表示されるので許可を押します。
Node-REDのリダイレクト先にアクセスして、認可コードが取得できます。
そして、その流れですぐにアクセストークンを取得します。Node-REDから認可コードを問い合わせて、freee APIからアクセストークンを取得します。
て以下の debug ノードに到達します。
グローバル変数のfreeeAccessTokenResponsePayloadに記録されます。
事務所情報 API を取得してみる
アクセストークンを取得できたので、こちらのフローで事業所一覧 API につなぎます。
inject ノードのこちらのボタンをクリックしましょう。
無事取得されて debug ノードにデータが到達し、サイドバーのデバッグタブにAPIから帰ってきた内容が表示されます。
リフレッシュトークン機能
freee APIのアクセストークンを取得する | freee Developers Community
成功すると、レスポンスとして 下記のようにアクセストークン、リフレッシュトークンが得られます。有効期限は、アクセストークンについては24時間、リフレッシュトークンでは無期限となっています。使用する際は必要に応じてrefresh、revokeしてください。
とのことで、freee API は1日でアクセストークンの期限が切れる仕様なので、一定時間でアクセストークンを更新する(リフレッシュトークン)する必要があります。
今回のインポートしたフローには、リフレッシュトークン機能も実装済みです。
ただ、あえてリフレッシュトークンを定期実行はさせていません。アクセストークンまわりがちゃんと動かせて、よし使うぞ!となるまでは inject ノードを都度都度クリックする形で実験できます。
こちらのフローにある inject ノードのこちらのボタンをクリックしましょう。
リフレッシュトークン取得されて debug ノードにデータが到達し、サイドバーのデバッグタブにAPIから帰ってきた内容が表示されます。
現状ではクリックをいちいちしないとリフレッシュトークンされないので、「もうこれで使い続けて行けそうだな」と思えたら inject ノードの定期実行を以下のように設定します。
このように、 指定した時間間隔
で時間間隔を 12時間
で設定してデプロイしましょう。 これで、 inject ノードをいちいちクリックすることなく、12時間ごとにリフレッシュされて、アクセストークンが維持されます。
実際やってみると Node-RED で freee API のデータが実際にとれるとワクワクするし、LINE Bot や Discord など他の仕組みにつないで、どんどん育てられるので面白いですね。
ぜひ試してみてくださいー。