Node-RED と Vue 3 が連携したプロジェクトで Vue 側に axios を導入し Node-RED http ノードと連携するメモです。
やりたいこと
Node-RED と Vue 3 が連携したプロジェクトに初期表示のコンポーネントと Bootstrap を導入するメモ
先日の Node-RED と Vue 3 が連携したプロジェクトをさらに発展です。いよいよ、Vue 側に axios を導入し、 Node-RED http ノードと連携させます。
2021/09/04 時点の情報で進めます。
Node-RED http ノードでデータのやり取りする API をつくる
http://localhost:18801/admin/
にアクセスして Node-RED でデータのやり取りする API をつくります。
このようなフローで、http://localhost:18801/api1
にアクセスすると
タイムスタンプを返答します。
インポートする実際に動作する JSON はこちらです。
[{"id":"357c66447b2fa80b","type":"http in","z":"381bab81f5f2e31e","name":"","url":"/api1","method":"get","upload":false,"swaggerDoc":"","x":160,"y":220,"wires":[["c6028d3ea1bc4d91"]]},{"id":"9f1b50a74ca8a564","type":"http response","z":"381bab81f5f2e31e","name":"","statusCode":"","headers":{},"x":530,"y":220,"wires":[]},{"id":"c6028d3ea1bc4d91","type":"change","z":"381bab81f5f2e31e","name":"タイムスタンプ","rules":[{"t":"set","p":"payload.timestamp","pt":"msg","to":"","tot":"date"}],"action":"","property":"","from":"","to":"","reg":false,"x":360,"y":220,"wires":[["9f1b50a74ca8a564"]]}]
(わりと大事)ターミナルの2つ目を起動する
Node-RED を動かしているターミナルをで他の作業をしてしまうと Node-RED が終了してしまうので、ターミナルの2つ目を起動します。私の場合は Visual Studio Code のターミナルで+ボタンをクリックして新しいターミナルを開きます。
Vue 側に axios を導入
cd vue-node-red-view
ターミナル 2 つ目で、View のプロジェクトフォルダ vue-node-red-view
に入ります。
axios だけでなく vue-axios をつかうようだということまでは分かったんですが、以前の Vue 2 や Vue CLI での振る舞いがこんがらがってしまい、まあまあハマったので、ここはやはり本家のページ vue-axios の README を参考に進めます。結果としてうまく動作しました。
こちらの記事も参考になりました。(ほかの人も苦しんでたんだという気持ち) → javascript – How to correctly import Axios in vue 3 after creating new project with CLI? – Stack Overflow
ということで。
npm i axios vue-axios
axios と vue-axios をインストールします。
main.js で axios を import
vue-node-red-view/src/main.js に書き換えます。
// 書き換え前 import { createApp } from 'vue' import App from './App.vue' import 'bootstrap' import 'bootstrap/dist/css/bootstrap.css' createApp(App).mount('#app')
こちらが以前のもので、書き換えた後のものはこちらです。
import * as Vue from 'vue' import App from './App.vue' import 'bootstrap' import 'bootstrap/dist/css/bootstrap.css' import axios from 'axios' import VueAxios from 'vue-axios' const app = Vue.createApp(App) app.use(VueAxios, axios) app.mount('#app')
これでプロジェクトで axios が使えるようになります。
NodeRED コンポーネントを書き換える
vue-node-red-view/src/components/NodeRED.vue を以下のように書き換えます。
<template> <div class="row"> <div class="row"> <div class="col-sm"> <h1>{{infoTitle}}</h1> <!-- 追記 --> <h3>{{timeStamp}}</h3> </div> </div> <div class="row"> <div class="col-sm"> <ul class="list-group"> <li class="list-group-item">An item</li> <li class="list-group-item">A second item</li> <li class="list-group-item">A third item</li> <li class="list-group-item">A fourth item</li> <li class="list-group-item">And a fifth one</li> </ul> </div> </div> </div> </template> <script> export default { name: 'NodeRED', props: { msg: String }, data () { return { infoTitle: "Node-RED INDEX", timeStamp: 0 } }, methods: { // 追記 getTimestamp:async function(){ const response = await this.axios.get('http://localhost:18801/api1'); console.log(response); this.timeStamp = response.data.timestamp; } }, mounted () { // 追記 this.getTimestamp(); } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> </style>
つまり、Node-RED の動いている 18801 ポートのAPI http://localhost:18801/api1
にデータを取得して {{timeStamp}}
表示するようにしました。
Node-RED の中だけで動くならルートパスでもいいのですが、Vue プロジェクト内で npm run serve
で起動する開発サーバー http://localhost:8080
から参照できるようにしています。
現時点では、以下のように開発を想定しています。
- ローカルで何かしらの仕組みを作り自分のツールとして使いたい場合
- 開発するとき
- Vue プロジェクト内で
npm run serve
で起動する開発サーバー 8080 ポートで使えるやつで開発する
- Vue プロジェクト内で
- ローカルで自分のツールとしての運用するとき
- 開発して落ち着いたら
npm run build
して Node-RED を起動して使う http://localhost:18801/~
以下で API が引けるので問題なく動作する
- 開発して落ち着いたら
- 開発するとき
ここまで環境を整えていて私の用途として話が混ざりがちだったんですが、「ローカルで作った仕組みを Heroku や他のサーバーに配置して動かす場合」の使い方はもう少し考えてやる必要がありそうです。
その場合、
- ローカルで作った仕組みを Heroku や他のサーバーに配置して動かす場合
- 開発するとき
- Vue プロジェクト内で
npm run serve
で起動する開発サーバー 8080 ポートで使えるやつで開発する(同様のやりかた) - ローカルでも Node-RED 18801 ポートのほうで作業するとなると
npm run build
をしまくらないといけないので、これだと作業が煩雑になるので改善の余地あり
- Vue プロジェクト内で
- Heroku や他のサーバーに配置して動かして自分のツールとしての運用するとき
- 開発して落ち着いたら
npm run build
して Node-RED を起動して使う(同様のやりかた) http://localhost:18801/~
でやってしまうと Node-RED 側に API が届かない(URLが違う)ので、ルートパスで引くか、現在のホスト名?を使って URL を作成してつなげる方法など検討- このあたり location.hostname をうまく使うとか → 第5回 問題を発生させにくくするURLの扱い方:JavaScriptセキュリティの基礎知識|gihyo.jp … 技術評論社
- 開発して落ち着いたら
- 開発するとき
となるので、別の記事で考えてみようと思います。いきなり、いろいろ想定を考えると手が進まなくなってしまいますからね。
今回の記事では、とりあえず「ローカルで何かしらの仕組みを作り自分のツールとして使いたい場合」で進めます。
試してみる
Vue プロジェクト内で npm run serve
で起動する開発サーバーで動かしてみます。 Node-RED が動くサーバー 1880 も動いている前提です。
なぜか、0 となってしまい、うまく動きません。
Access to XMLHttpRequest at ‘http://localhost:18801/api1’ from origin ‘http://localhost:8080’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
そうだ、ブラウザでアクセスするので同じ localhost サーバーとはいえポート違いなので CORS エラーが発生してました。
CORS 調整する
ということで、Node-RED 側の setting.js のほうで同じ localhost サーバーでやり取りできるように CORS を調整します。
- expressjs/cors: Node.js CORS middleware
- なんとなく CORS がわかる…はもう終わりにする。 – Qiita
https://github.com/expressjs/cors#configuration-options
あたりを参考にしながら、httpNodeCors パラメータを以下のように修正して、HTTP Inノードの HTTP エンドポイントの CORS を有効にします。
httpNodeCors: { origin: [ "http://localhost:8080" ], methods: "GET,PUT,POST,DELETE" },
書き換えたら、Node-RED を再起動してみましょう。こういうとき、再起動を忘れて CORS エラー治らない!ってなりがちなので、意識して再起動を心掛けましょう(自戒)
無事、データが取得されて 1630879697270
というタイムスタンプの値で置き換わっています。
実際やってみると、いろいろ注意点が見えてきますね
フロントエンドとバックエンド行き来する構造を考えると、 CORS や開発と運用のサーバー配置など気にするところが分かってきますね。こういう試行錯誤が楽しいです。
たしかに、過去にフロントエンドだけだと CORS まわりでツラい思いをすることは多く、Node-RED というか Node.js 系のサーバーサイドプログラムで API 取得して CORS 気にしなくていいのは初見で感動したなーというのを、いまさらながらに思い出しました。
ひとまず、API をつくって表示側と連携することができました!