全端實作-MEVN

整合練習Mongo DB, Express JS, Vue JS, Node

Jacy Chu
16 min readMar 7, 2024

結合Mongo DB, Express JS, Vue JS, Node來實作一個TodoApp。本文前半段內容參考Art of Engineer教學影片:Build app using Vue JS, Node Express JS and Mongo DB (MEVN Stack)。影片前半有介紹Mongo DB資料庫創建,以及使用Express完成get, post, delete api撰寫,後半有介紹Vue3引用方式,這部分筆者改用Vue-cli去搭建。除了影片的內容,筆者延伸研究put api功能,並加入modal去完成簡易的TodoApp(增刪改查)。

📋 目錄

  1. 安裝前置準備
  2. 安裝資料庫
  3. 創建後端用node, express JS(get, post, delete api)
  4. 創建Vue專案
  5. 加入put api及刻modal
  6. 小結

安裝前置準備

安裝好vscode
安裝好nodejs

可以用 node -v 查看版本號 須在v5.5上

安裝資料庫

Mongo DB官網:Mongo DB

申請帳號密碼

選取方案時選擇Free的方案

資料庫的名稱命名為:todoappdb,底下建立 todoappcollection

點選Database新增資料,新增id, description

介紹node, express JS

此段落參考:MDN docs Express/Node introduction

Node.js 是一個開源、跨平台和允許開發者使用 Javascript 創造伺服器端工具和應用的執行環境。運行的目的是為了能在瀏覽器外使用,例如:直接執行在電腦或伺服器上。所以該環境捨棄了瀏覽器限定的 JavaScript APIs 並增加更多傳統 OS APIs 的支援,例如:HTTP 和檔案系統的程式庫。

// 載入 HTTP 模組
var http = require("http");

// 創建 HTTP 伺服器並監聽8000 port
http
.createServer(function (request, response) {
// Set the response HTTP header with HTTP status and Content type
response.writeHead(200, { "Content-Type": "text/plain" });

// Send the response body "Hello World"
response.end("Hello World\n");
})
.listen(8000);

// Print URL for accessing server
console.log("Server running at http://127.0.0.1:8000/");

Node 並不原生支持其他常見的 web 開發任務,如果你想為不同的 HTTP 方法(例如:GET, POST, DELETE等)增加特定的處理、替不同的 URL 路徑提供靜態檔案、使用樣板或動態性的產生 response,你需要自己完成相關的程式或者是避免重新造輪子 - 使用 web 框架!

Express 是最受歡迎的 Node web 框架,還是其他許多流行的Node web 框架的底層庫,它提供:

  • 替不同 HTTP Method、不同 URL 路徑的 requests 編寫不同的處理方法
  • 透過整合「畫面」的渲染引擎來達到插入資料到樣板中產生 response
  • 設定常見的 web 應用設定,例如:連線用的 port 和產生 response 的樣板位置
  • 在 request 的處理流程中增加額外的「中間層」(middleware) 進行處理

開發者們已經創造相容的中間層套件來解決大部份 web 開發的問題,這些套件能處理 cookies, sessions,登入,URL 參數,POST 資料,安全標頭等等,你能在Express Middleware中找到這些套件的列表(以及其他流行的第三方套件)

var express = require("express");
var app = express();

app.get("/", function (req, res) {
res.send("Hello World!");
});

app.listen(3000, function () {
console.log("Example app listening on port 3000!");
});

創建後端用node, express JS

新建一個mevn-todo資料夾,並在其底下新增api資料夾,終端機輸入指令

> npm init

> npm install express — save

> npm install cors

> npm install mongodb@4.1.0 — save

> npm install multer — save (Multer 為一個 node.js middleware 主要用於上傳檔案)

新增index.js檔把所有安裝的都引用進來

var Express = require("express");
var Mongoclient = require("mongodb").MongoClient;
var cors = require("cors");
const multer = require("multer");

var app = Express();
app.use(cors());

把connection資訊貼上,修改<password>成自己的

var CONNECTION_STRING = "mongodb+srv://jacy:<password>@cluster0.olhpylg.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0"
var DATABASENAME = "todoappdb"
var database;

app.listen(5038,()=>{
Mongoclient.connect(CONNECTION_STRING,(error,client)=>{
database=client.db(DATABASENAME);
console.log("Mongo DB Connection Successful")
})
})

設定DATABASENAME為 todoappdb

此時在終端機cd到api資料夾路徑下

輸入 node index.js

出現 Mongo DB Connection Successful字樣

接下來加入get, post, delete的api

app.get('/api/todoapp/GetNote',(request, response) => {
database.collection("todoappcollection").find({}).toArray((error, result)=>{
response.send(result);
})
})

app.post('/api/todoapp/AddNote',multer().none(),(request,response)=>{
database.collection("todoappcollection").count({},function(error,numOfDocs){
database.collection("todoappcollection").insertOne({
id:(numOfDocs+1).toString(),
description:request.body.newNotes
});
response.json("Add Successfully")
})
})

app.delete('/api/todoapp/DeleteNote',(request,response)=>{
database.collection("todoappcollection").deleteOne({
id:request.query.id
});
response.json("Delete Successfully")
})

創建Vue專案

教學影片中使用cdn方式引入Vue還有axios套件,這邊筆者改用vue-cli實作。有關安裝步驟可以參考這篇文章前半部:

🚪 用 Vue Router 實作網頁切換(上)

在mevn-todo資料夾,並在其底下新增ui資料夾,可在此安裝Vue

Vue.js 本身沒有內建方法呼叫 API ,推薦使用 axios 來完成 ajax 請求。axios 是一個基於 Promise 的 HTTP 庫,可以在瀏覽器和 node.js 中使用。

有關axios介紹可以參考這篇文章:
🚪 Vue.js + axios 介紹與實作

template部分將預設的img以及HelloWorld元件移除,加入下方程式碼:

<template>
<div>
<h3>Todo App</h3>
<input id="newNote"/>&nbsp;
<button @click="addNewNote()">Add Note</button>
<p v-for="note in notes" :key="note.id">
<b>* {{ note.description }}</b>
<button @click="deleteNote(note.id)">Delete Note</button>
</p>
</div>
</template>

script部分改成如下:

<script>
import axios from 'axios'

const API_URL = "http://localhost:5038/";

export default {
name: 'App',
data() {
return {
title: "Todo App",
notes: [],
}
},
methods: {
async refreshData() {
axios.get(API_URL+"api/todoapp/GetNote")
.then((response)=>{
this.notes=response.data;
})
},
async addNewNote() {
var newNote = document.getElementById("newNote").value;
const formData = new FormData();
formData.append("newNotes", newNote);
axios.post(API_URL+"api/todoapp/AddNote", formData)
.then((response)=>{
this.refreshData();
alert(response.data);
})
},
async deleteNote(id) {
axios.delete(API_URL+"api/todoapp/DeleteNote?id="+id)
.then((response)=>{
this.refreshData();
alert(response.data);
})
}
},
mounted: function() {
this.refreshData();
}
}
</script>

從終端機下指令npm run serve跑起來

網址輸入http://127.0.0.1:8080/

目前完成查看、新增、刪除三個功能

加入put api及刻modal

參考mongoDB官網

提供 db.collection.updateOne()這個function 來完成更新資料的動作

api/index.js加入 put api

app.put('/api/todoapp/updateNote',(request,response)=>{
database.collection("todoappcollection").updateOne({
id:request.query.id
},{
$set: {description: request.query.description }
});
response.json("Update Successfully")
})

切回前端App.vue檔案
先新增DescriptionModal.vue檔案,內容如下:

App.vue 加入update btn以及DescriptionModal

  <div>
<h3>Todo App</h3>
<input id="newNote"/>&nbsp;
<button @click="addNewNote()">Add Note</button>
<p v-for="note in notes" :key="note.id">
<b>* {{ note.description }}</b>&nbsp;
<button @click="deleteNote(note.id)">Delete Note</button>
<!-- 加入update btn -->
<button @click="showModal(note.id)">Update note</button>
</p>
<!-- 加入DescriptionModal -->
<DescriptionModal :show="modalVisible" :noteid="noteid" title="Update Description" @close="hideModal" @submit="(eventPayload) => updateDesciption(eventPayload)">
</DescriptionModal>
</div>

script部分多引用DescriptionModal元件還有加入三個function

<script>
import axios from 'axios';
// 引用 DescriptionModal
import DescriptionModal from './DescriptionModal.vue';

const API_URL = "http://localhost:5038/";

export default {
name: 'App',
components: {
DescriptionModal
},
data() {
return {
title: "Todo App",
notes: [],
modalVisible: false,
// 新增noteid
noteid: "",
}
},
methods: {
async refreshData() {
axios.get(API_URL+"api/todoapp/GetNotes")
.then((response)=>{
this.notes=response.data;
})
},
async addNewNote() {
var newNote = document.getElementById("newNote").value;
const formData = new FormData();
formData.append("newNotes", newNote);
axios.post(API_URL+"api/todoapp/AddNote", formData)
.then((response)=>{
document.getElementById("newNote").value = "";
this.refreshData();
alert(response.data);
})
},
async deleteNote(id) {
axios.delete(API_URL+"api/todoapp/DeleteNote?id="+id)
.then((response)=>{
this.refreshData();
alert(response.data);
})
},
// 新增showModal, hideModal, updateDescription
showModal(id) {
this.modalVisible = true;
this.noteid = id;
},
hideModal() {
this.modalVisible = false;
},
async updateDescription(params) {
this.modalVisible = false;
axios.put(API_URL+"api/todoapp/updateNote?id="+params.id+"&description="+params.des)
.then((response)=>{
this.refreshData();
alert(response.data);
})
}
},
mounted: function() {
this.refreshData();
}
}
</script>

從終端機下指令npm run serve跑起來

網址輸入http://127.0.0.1:8080/
搭配查看 mongoDB

小結

從實作中,先建立MongoDB,再搭建node express去串DB寫api,用Vue寫前端,完成完整的功能。對於前後端的認識更加全面。

--

--