Node-RED と Vue 3 が連携したプロジェクトで Vue 側に axios を導入し Node-RED http ノードと連携するメモ

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 をつくる

image

http://localhost:18801/admin/

にアクセスして Node-RED でデータのやり取りする API をつくります。

image

このようなフローで、http://localhost:18801/api1 にアクセスすると

image

タイムスタンプを返答します。

インポートする実際に動作する 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つ目を起動する

image

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 ポートで使えるやつで開発する
    • ローカルで自分のツールとしての運用するとき
      • 開発して落ち着いたら npm run build して Node-RED を起動して使う
      • http://localhost:18801/~ 以下で API が引けるので問題なく動作する

ここまで環境を整えていて私の用途として話が混ざりがちだったんですが、「ローカルで作った仕組みを Heroku や他のサーバーに配置して動かす場合」の使い方はもう少し考えてやる必要がありそうです。

その場合、

  • ローカルで作った仕組みを Heroku や他のサーバーに配置して動かす場合
    • 開発するとき
      • Vue プロジェクト内で npm run serve で起動する開発サーバー 8080 ポートで使えるやつで開発する(同様のやりかた)
      • ローカルでも Node-RED 18801 ポートのほうで作業するとなると npm run build をしまくらないといけないので、これだと作業が煩雑になるので改善の余地あり
    • Heroku や他のサーバーに配置して動かして自分のツールとしての運用するとき

となるので、別の記事で考えてみようと思います。いきなり、いろいろ想定を考えると手が進まなくなってしまいますからね。

今回の記事では、とりあえず「ローカルで何かしらの仕組みを作り自分のツールとして使いたい場合」で進めます。

試してみる

Vue プロジェクト内で npm run serve で起動する開発サーバーで動かしてみます。 Node-RED が動くサーバー 1880 も動いている前提です。

image

なぜか、0 となってしまい、うまく動きません。

image

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 を調整します。

あたりを参考にしながら、httpNodeCors パラメータを以下のように修正して、HTTP Inノードの HTTP エンドポイントの CORS を有効にします。

    httpNodeCors: {
        origin: [ "http://localhost:8080" ],
        methods: "GET,PUT,POST,DELETE"
    },

書き換えたら、Node-RED を再起動してみましょう。こういうとき、再起動を忘れて CORS エラー治らない!ってなりがちなので、意識して再起動を心掛けましょう(自戒)

image

無事、データが取得されて 1630879697270 というタイムスタンプの値で置き換わっています。

実際やってみると、いろいろ注意点が見えてきますね

フロントエンドとバックエンド行き来する構造を考えると、 CORS や開発と運用のサーバー配置など気にするところが分かってきますね。こういう試行錯誤が楽しいです。

たしかに、過去にフロントエンドだけだと CORS まわりでツラい思いをすることは多く、Node-RED というか Node.js 系のサーバーサイドプログラムで API 取得して CORS 気にしなくていいのは初見で感動したなーというのを、いまさらながらに思い出しました。

ひとまず、API をつくって表示側と連携することができました!