STM32LoRa ตอนที่ 3.1 อธิบาย Code บางส่วนของ Application Server ที่สร้างไว้ (Backend และ Frontend)

จากในบทความตอนที่ 3 ผมได้ทำการสร้าง Application Server ไว้ แต่ไม่ได้อธิบายที่มาที่ไปของมัน มาในตอนนี้ผมจะอธิบายโค๊ดในส่วนที่สำคัญของมันนะครับ

<< ตอนที่ 3 : ส่งข้อมูลจาก Network Server ไปยัง Application Server

ภาพรวมของ Application Server

แบ่ง Application Server ออกเป็น Frontend และ Backend

  1. Backend เป็น NodeJS Server โดยใช้ ExpressJS Framework
  2. Frontend เป้น React Framework

โดยทั้งสองรับส่งข้อมูลหากันผ่าน JSON Data ซึ่งในขั้นตอนการพัฒนาโปรแกรมนั้นเราจะรัน Frontend ที่ Port 3000 และ Backend ที่ Port 5000 และมีการใช้ Proxy Library ที่ Frontend เพื่อให้เราสามารถเรียกใช้ Backend ผ่าน Port 3000 ของ Frontend ได้เลยทำให้ง่ายในการพัฒนาระบบ เพราะเมื่อเรานำมันไป Deploy ใน Heroku เราจะต้องทำการ Build Frontend ออกมาเป็นไฟล์ชนิด Static Website และให้รันอยู่ใน Port 80 พร้อมกันกับ Backend

Download : https://github.com/choonewza/stm32lora_loraiot_ep3

Backend

โครงสร้างไฟล์ /loraiot_server สำหรับเป็น Backend โดยใช้ Express Framework

//loraiot_server folder
loraiot_server
--app
----controllers
------loraiotController.js
----models
------Sensor.js
----routes
------loraiotRoutes.js
--client
--config

----keys.js
----keys-dev.js
----keys-prod.js
--node_modules
--services

----mongoose.js
.gitignore
index.js
package.json
yarn.lock

ไฟล์ /loraiot_server/package.json

ไฟล์ /loraiot_server/package.json

คำสั่ง engines จะถูกใช้ใน Heroku เพื่อบอก Heroku ว่าเราจะใช้ engines เวอร์ชั่นไหนในการ Run Codes ที่อัปโหลดขึ้นไปนี้

"engines": {    
"node": "10.15.0",
"npm": "6.5.0"
},

คำสั่ง scripts เป็นคำสั่งเพื่อใช้เริ่มการทำงานของตัว Backend นี้

"start": "node index.js"
----> คำสั่งสำหรับ start ระบบแบบปกติ ($ npm start)
"server": "nodemon index.js"
----> คำสั่งสำหรับ start ระบบในตอน dev เพราะมีการใช้ nodemon เพื่อให้เวลาเราแก้ไขไฟล์แล้วจะทำการ reload แบบระบบให้ใหม่อัตโนมัติ ($ npm run server)
"client": "npm run start --prefix client"
----> คำสั่งสำหรับ start ระบบ frontend โหมด dev ผ่าน command line ในหน้า backend ($ npm run client)
"dev": "concurrently \"npm run server\" \"npm run client\""
----> คำสั่งสำหรับ start ระบบในตอน dev โดยจะรันทั้ง backend และ frontend ในเวลาเดียวกัน โดยมีการใช้ concurrently เป็นตัวช่วยให้สามารถรันพร้อมกันได้
"heroku-postbuild": "NPM_CONFIG_PRODUCTION=false npm install --prefix client && npm run build --prefix client"
----> คำสั่งสำหรับ deploy ขึ้น server ของ Heroku จะทำให้สามารถ build โค๊ดของ react ซึ่งเป็น frontend หลังจาก upload ไฟล์ขึ้นระบบได้

ในส่วนของ Dependencies จะบอกว่าเราใช้ Library ตัวไหนบ้างในโปรเจคนี้

"dependencies": {
   "body-parser": "^1.18.3"
---> ทำให้เว็บเราอ่านและส่งค่าของ Body ใน HTTP Request ได้
   "compression": "^1.7.3"
---> ไว้บีบอัดข้อมูลที่ส่งออกจาก Backend ให้มีขนาดเล็กลง โดยใช้ใน Production Mode
  "concurrently": "^4.1.0"
---> เนื่องจากในขึ้นตอนพัฒนา (Dev mode) เราแบ่งการสร้างเป็น Frontend และ Backend ที่มีการรันระบบแยกกัน เราจะใช้ library นี้ทำการรันพร้อมกันผ่านคำสั่ง npm run dev
  "cookie-session": "^2.0.0-beta.3"
---> ใช้เก็บข้อมูลใน Cookie
  "express": "^4.16.4"
---> framework ในการพัฒนาช่วยให้สร้าง NodeJS Server ได้ง่ายขึ้น
  "moment": "^2.23.0"
---> ช่วยในการจัด Format และคำนวนเวลา
  "mongoose": "^5.4.1"
---> ใช้ในการติดต่อกับ MongoDB
  "morgan": "^1.9.1"
---> ใช้แสดง log การเข้าถึง Server ในตอน Dev mode
  "nodemon": "^1.18.9"
---> ทำให้ Server รันใหม่อัตโนมัติเมื่อมีการแก้ไขโค๊ด
},

ไฟล์ /loraiot_server/index.js

จะเป็นไฟล์แรกที่ระบบเริ่มทำงาน โดยจะมีการเตรียมการใช้งาน Library ต่างๆ ในนี้ โดยโค๊ดที่น่าสนใจคือ

//loraiot_server/index.js
if (process.env.NODE_ENV === "production") {
//Express will serve up production assets
//like our main.js file, or main.css file!
app.use(express.static("client/build"));
//Express will serve up the index.html file
//if it doesn't recognize the route
const path = require("path");
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
});
}
//*** เป็นส่วนของคำสั่งที่จะทำงานเมื่อเป็น Production Mode บอกว่า เมื่อได้รับ Request จาก URL ที่ไม่ตรงกับใน Routes ที่ถูกประกาศก่อนหน้านี้จะให้ทำการส่งไฟล์หน้าแรกของ Frontend ที่สร้างจาก React Framework และผ่านการ Build แล้วออกไป

ไฟล์ /loraiot_server/app/routes/loraiotRoutes.js

เป็นไฟล์ที่ใช้กำหนด URL ที่เราจะใช้ใน Backend ของเรา

//loraiot_server/app/routes/loraiotRoutes.js
module.exports = app => {
const route = require("./../controllers/loraiotController");
  app.post("/api/iot/receiver", route.store);
//-->ใช้สำหรับรับ Request จาก Network Server ที่จะทำการ Forword ข้อมูลมาให้ผ่าน Http POST
  app.get("/api/iot-received", route.list);
//-->ใช้สำหรับส่งค่าที่เก็บไว้ใน NoSQL Database ออกไปให้ Frontend ในรูปแบบของ JSON Data
};

ไฟล์ /loraiot_server/app/controllers/loraiotController.js

เป็นไฟล์ควบคุมการทำงานของ Server โดยจะมีสอง Function การทำงานคือ

1. exports.store = async (req, res, next) => {…}

จะทำการรับข้อมูลที่ถูกส่งมาจาก Network Server แล้วนำเข้าไปเก็บไว้ใน Sensor Collection ของ Database (MongoDB of mlab)

ข้อมูลที่เก็บลง Sensor Collection ใน mlab

2. exports.list = async (req, res, next) => {…}

จะทำการอ่านค่า Sensor จากที่เก็บไว้ใน Database แล้วส่งออกไปในรูปแบบของ JSON Data เมื่อ Frontend มีการร้องขอเข้ามา

ข้อมูล JSON โดยส่งค่ากลับเป็น Array

ไฟล์ /loraiot_server/config/keys.js

เป็นไฟล์ที่เก็บค่าคงที่ที่ใช้ในโปรแกรมโดยจะเก็บ mongoURI ที่ใช้บอกตำแหน่งของ Database ที่เราจะใช้งาน โดยผมทำการแยก Key ออกเป็น 2 ชุดตามการใช้งาน คือ Development Key (keys-dev.js) และ Production Key (keys-prod.js) ซึ่งไฟล์ /loraiot_server/config/keys.js จะมีหน้าที่เลือกชุด Key ที่จะนำมาใช้งานตาม Enviroment Variable ที่เรา set ไว้ในตัวแปร NODE_ENV โดยที่ถ้าเป็นในโหมด Production เราจะใช้ Key ที่ถูกเซ็ตไว้ใน Enviroment Variable ของเครื่อง Server แต่ถ้าเป็น Developmode เราก็จะใช้ Key ที่ถูกใส่ไว้ใน ไฟล์ keys-dev.js ได้โดยตรงเลย


Frontend

ผลการค้นหารูปภาพสำหรับ react js

โครงสร้างไฟล์ /loraiot_server/client สำหรับเป็น Frontend โดยใช้ React Framework ผ่าน create-react-app

//client folder
client
--public
----favicon.ico
----index.html
----manifest.json
--src
----components
----screens

------SensorDataReceiver.js
----App.css
----App.js
----index.css
----index.js
----logo.svg
----serviceWorker.js
----setupProxy.js
.gitignore
package.json
README.md
yarn.lock

ไฟล์ /loraiot_server/client/package.json

"dependencies": {
"axios": "^0.18.0",
"http-proxy-middleware": "^0.19.1",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"react-router-dom": "^4.3.1",
"react-scripts": "2.1.2"
},

ที่น่าสนใจคือมีการใช้ http-proxy-middleware เพื่อทำ Proxy เนื่องจากการใช้ react-scripts เวอร์ชั่นตั้งแต่ 2 ขึ้นไป เราไม่สามารถใช้คำสั่ง proxy ใน package.json แบบเดิมได้ต้องใช้ 3rdParty ช่วยในการทำ โดยต้องสร้างไฟล์ setupProxy.js ไว้ใน /loraiot_server/client/src/setupProxy.js โดยไฟล์นี้เราจะใช้กำหนด URL ของ Backend ที่เราจะใช้งานใน Frontend ทำให้เราไม่จำเป็นต้องอ้างถึง Port 5000 ของ Backend เพราะถ้า URL ที่เข้ามามี Part ตรงกับที่เราระบุไว้มันจะทำการส่งต่อไปยัง URL ของ Backend เองเลย

//loraiot_server/client/src/setupProxy.js
const proxy = require("http-proxy-middleware");
module.exports = function(app) {
app.use(proxy("/auth/google", { target: "http://localhost:5000" }));
app.use(proxy("/api/*", { target: "http://localhost:5000" }));
};

โดยเราจะเขียน UI ไว้เพียงหน้าเดียวคือหน้าแสดงรายการของ Sensor ที่เก็บไว้ใน Database โดยโค๊ดจะอยู่ในไฟล์ /loraiot_server/client/src/screens/SensorDataReceiver.js

แสดงหน้าแรกของเว็บไซต์

ในบทความคั่นกลางนี้ก็จบแล้วครับ