iT邦幫忙

2022 iThome 鐵人賽

DAY 13
2

Day13 自己做一個價值幾十萬的動態網站

第十三課:Api串接 建立schema CRUD的練習與熟悉 part1

了解Api route連接 與express.router的運用

首先我們昨天也稍微講解到app.get 而目前app開頭都是已經在使用我們先宣告的const app = express() 的express語法

app.get('/', (req, res) => {
  res.send('Hello World!')
})


了解後,這邊我們可以先導入這個式子並輸入上面寫的url地址也就是"/" 完整預設的http://localhost:5000/來看看這個函數有沒有被啟動,可以用這個api來執行這個效果。


但這邊我們將會創立新的資料夾來放這些get method與之後的post等等的,因為都放在index.js的話,我們有四個分類auth, user, hotel, rooms
也就是常見的管理員負責更新飯店資料的、使用者用戶跟飯店與房間(也就是產品)所以這邊熟悉以後任何產品向的訂購等等預購都可以用這套模式去轉換,而四個分類裡面又都有上傳、修改、讀取資料,要是都寫在index.js會爆炸,所以我們將另開一個folder來存放這些api並分門別類它們

然後一開始我們先隨便從ApiRoutes的hotel.js 開始

在ApiRoutes的hotel.js打上

import express from "express"
const router = express.Router()
//get 就像app.get 運用router把app.get可以分門別類 
router.get("/",(req,res)=>{
    res.send("這邊是hotelsApi End points連接點")
})
export default router

應用到的Router會是下方/api/v1/hotels 從的下半部分延伸
這邊列/api/v1/hotels 是代表api 然後版本version1 然後是專屬hotels的api,所以之後往下專門在處理hotel的api都會寫在這裡,如可能在這邊找所有的飯店資料、單一資料與更改上傳飯店資料等等的。

如果一樣用結點的概念圖就像下面那樣


然後這邊就可以用這種方式分別導入ApiRoute並測試各個Api Path有沒有部署成功
在index.js裡面 分別引入下列這些

import hotelsApiRoute from "./ApiRoutes/hotels.js"
import roomsApiRoute from "./ApiRoutes/rooms.js"
import usersApiRoute from "./ApiRoutes/users.js"
import authApiRoute from "./ApiRoutes/auth.js"
app.use("/api/v1/hotels",hotelsApiRoute)
app.use("/api/v1/rooms",roomsApiRoute)
app.use("/api/v1/users",usersApiRoute)
app.use("/api/v1/auth",authApiRoute)

然後在各字的資料夾如./ApiRoutes/auth.js folder裡面可以都改成相對應的示範Api get method

import express from "express"
const router = express.Router()
//get 就像app.get 運用router把app.get可以分門別類 
router.get("/",(req,res)=>{
    res.send("這邊是AuthApi End points連接點")
})
export default router

"這邊是AuthApi End points連接點" 中間就可以自由替換成不同的Api
然後回到hotel.js 我們要來徹底了解與實作CRUD等資料運作

了解CRUD 並上傳與第一筆資料


首先我們要來創立第一筆hotel資料,所以會用到create method
使用到的事post()函數,所以post 他會需要有你創立的資料的內容,你必須自己打上去,就像是貼文那樣,跟可以設置你成功創建資料後,回傳的創建成功提醒。所以req 為hotel資料, res 為創建成功提醒或是你創建的資料再回傳一次查閱看是不是像你想要創建的資料一樣。
所以這邊我們要先確認每次上傳的資料都是有原則性的,所以跟所有資料庫的起頭都一樣,我們要先設立我們的資料型態,使用到的是mongoose.Schema套件,所以首先一樣先創立一個folder把所有需要的資料model都創建在裡面。

這邊總共需要創立3個model user hotel room,並user還要分成一般用戶與管理員等等的,所以一開始創立資料型態時都要考慮進去,但nosql的優勢就是可以很自由的編輯資料型態,所以之後如果發現想要重新更改都是可以且快速的,如想將照片欄位改變成上傳不一定要要求的的等等。
下方附上各個資料型態的schema HotelSchema 並一一解說,這些之後會應用到的部分

const HotelSchema = new mongoose.Schema({
    name:{
        type:String,//住宿名稱
        required:true,//一定需要
    },// required 加上去後如果post上去沒有這欄,會上傳失敗
    type:{
        type:String,//住宿型態,到時候會有飯店、公寓、民宿等等的
        required:true,//產品也常常會需要這種分類
    },
    city:{
        type:String,//同於地址的城市,將會特別列出來
        required:true,//到時候有不同的城市如台北就可以用這個來filter
    },
    address:{
        type:String,//在城市下的確切子資料,將會在hotelPage等完整資訊顯示
        required:true,
    },
    distance:{
        type:String,//這欄可寫可不寫是模擬到市中心的距離
        //為同為資料描述的同一種
    },
    photos:{
        type:[String],//很重要的住宿照片一環 因為會有很多照片所以使用array
       required:true,
    },
    title:{//對這個飯店的標題,可為甚麼 花蓮第一景觀民宿等等的
        type:String,
        required:true,
    },
    desc:{//對這個飯店的詳細描述,稍微介紹飯店規格啊形式等等的
        type:String,
        required:true,
    },
    rating:{//到時候的最重要評分,將會可以紀錄這個飯店的評價好壞
        type:Number,//型態就跟上面不同為數字 並可以打分0~10
        min:0,
        max:10, 
    },
    rooms:{
        type:[String],//住宿的衍伸資料,房間到時候就是必須紀錄這些房間能不能被訂房
        //這邊原本應該是要required:true,必須要的但跟下面的comment一樣我們會拉出來room做model
        //所以可以等room確認創建好後在這邊做串連
        //所以就先不用required:true
    },
    cheapestPrice:{//住宿的價格 這邊為什麼不是用price
        type:Number,//是因為通常到時候真正要顯示的是room不同房型的價格
        required:true,  //這邊搜尋後希望是通常都會展示最便宜的那個房型價格
    },//所以會跟rooms的價格串接,而這邊就是那邊最便宜得那一個
    popularHotel:{
        type:Boolean,//這個是為了到時候配合homPage我們的放在我們的最熱門住宿那邊,就像產品最熱門銷售的概念,設定如果為true這個資料就可以放上熱門榜
        default:false,
    },
    comments:{ //先紀錄到時候有的comment數 並如果要在創建comment可以再開一個model 並在裡面用一樣的方式紀錄是哪一個user留的與他等等的詳細資料
        type:Number,
        default:0,
    }
})
export default mongoose.model("Hotel",HotelSchema)

UserSchma 使用者的資料結構

import mongoose from 'mongoose';
const UserSchma = new mongoose.Schema({
    username:{//用戶名稱
        type:String, 
        required:true,//必須上傳
        unique:true,//且不能重複,並免同樣姓名的使用者
    },
    email:{
        type:String,
        required:true,
        unique:true,//信箱也是必須獨一無二
    },
    password:{
        type:String,//密碼
        required:true,
    },
  isAdmin:{
        type:Boolean,//是否為後台管理員身份 所以不一定要先填
        default:false,//就不用 required:true, 只要創建管理員身份時在特別上傳這部分
    },
},{timestamps:true}) //這邊比hotels多了一個timestamps是為了紀錄創建時間戳章,通常後台會需要多再看到user者的創建時間
export default mongoose.model("User",UserSchma)

RoomSchema

import mongoose from 'mongoose';
const RoomSchema = new mongoose.Schema({
    title:{ //房間名稱 如海景房
        type:String,
        required:true,
    },
    desc:{//房間描述 如雙人床與獨立衛浴
        type:String,
        required:true,
    },
    price:{//此房型的價格
        type:Number,
        required:true,
    },
    maxPeople:{//可以住幾人
        type:Number,
        required:true,
    },
    roomNumbers:[{ //這邊是表示到時候的房型編號 如可能房型都是海景房 有分01,02
       number:Number, unavailableDates:[{type:Date}]
    }],//與紀錄01,02被訂走的時間,為到時候calendar紀錄的時間
},{timestamps:true})
export default mongoose.model("Room",RoomSchema)

將上面都列好後,所以回到我們到ApiRoute的hotel.js我們要來利用這些model來創建資料
並先import hotel model來 記得要加上.js

import Hotel from "../models/Hotel.js" 記得.js

與第一個post method

router.post("/",async(req,res)=>{
    const newHotel = new Hotel(req.body) 
    try {
        const saveHotel = await newHotel.save()
        res.status(200).json(saveHotel)
    } catch (error) {
        res.status(500).json(error)
    }
})

這邊解釋一下 const newHotel = new Hotel(req.body) 首先我們知道了req而這邊新的body 就是我們傳輸資料的主體概念,所以意思是根據hotel model創立一個符合我們資料結構的第一筆資料,並運用上前一天教的try{}catch{}使用async配上await 代表如果newHotel可以被資料庫儲存的話,下面回報個成功status200代表回傳成功,而失敗就傳伺服器status500錯誤訊息回報。為了更瞭解body是什麼跟為什麼是req.body上傳這樣的寫法,我們先來了解postman與insomnia兩個專門處理api指令等軟件。

了解insomnia與如何實作

當我們在上面實作post()資料時,我們會發現跟get()不一樣,因為我們可以用一串url如http://localhost:5000/api/v1/hotels/find/123123 來get想要的資料,但當我們想要上傳資料時,卻不能用瀏覽器反向操作回去,因為那個就是後台架設好使用到api我們才會有一個平台能夠上傳資料到資料庫,所以insomnia與postman就解決了這些問題,兩個都可以使用這邊我們選用insomnia來實作。附上官方下載連接
https://insomnia.rest/download







所以這邊附上測資示範資料

{
	"name":"七季公寓酒店",
	"type":"飯店式公寓",
	"city":"布達佩斯",
	"address":"1061 布達佩斯, Király utca 8., 匈牙利",
	"distance":"地鐵站(Deak Ferenc tér)僅 100 公尺",
        "photos":"https://cf.bstatic.com/xdata/images/hotel/max500/40890517.jpg?k=cd55de5463af8988f78fd675904a43d02f77570debe9977c4c1fe658030b6d29&o=",
	"title":"7Seasons Apartments Budapest(布達佩斯七季公寓酒店",
	"desc":"預訂布達佩斯七季公寓酒店可享 Genius 折扣!只要登入,預訂此住宿即可省一筆。7Seasons Apartments 位於布達佩斯市中心,距離主要的大眾交通樞紐戴阿克‧費倫茨廣場地鐵站(Deak Ferenc tér)僅 100 公尺,設有 24 小時接待櫃台,提供寬敞的單臥室至三臥室公寓。所有公寓均提供免費無線網路。每日清潔服務和接駁服務需事先提出要求,並額外收費。所有公寓均設有廚房,可供自炊,且 7Seasons Apartments 周邊咖啡館、餐廳和 24 小時超市林立。步行即可輕鬆抵達聖伊什特萬聖殿(St. Stephen's Basilica)、菸草街會堂(The Great Synagogue)、匈牙利國家歌劇院和匈牙利國會大廈。此區為布達佩斯的人氣推薦區域(依據真實住客評語)情侶特別喜歡這個位置-並給他們的雙人旅行住宿體驗 9.7 分",
	"rating":9.6,
	"cheapestPrice":7903,
	"popularHotel": true,
	"comments":463
}

與在post url打上的http://localhost:5000/api/v1/hotels/

並在傳輸前在index.js打上

app.use(express.json())



並後續的操作都會應用到我們成功創建資料的這些資料id(畫面上是 _id)去做識別,利用它就可以做我們想要的CRUD

恭喜這邊就完成了第一次的資料上傳與串接

結論

Api在整個動態網站中扮演著很重要的角色,要跟前端配合來制訂我們的資料型態,要能涵蓋到所有的功能與實際到時候會操作到的情況,但一開始對這個產業經驗不足或是沒有固定的模組讓你參考時,很常會東漏一點西漏一點,所以對傳統的資料庫來說,後續要修改就會很致命,但mongoDB得好處是,讓我們可以到後續修改都很方便,不管是各自的資料要怎麼串接在一起,還是在我們的資料庫中新增新的資料項目,如雖然這邊還沒做,但我們其實後續也會加上的order訂單,就跟產品下單一樣,我們會想要管理我們的訂房訂單,讓他可以紀錄使用者username、訂房時段(前端使用者操作記錄)與訂房的房間與飯店,所以將結合user,hotel,room等資料的一筆新資料,或是也可以直接回傳user與room配搭到時候的時間區間的合併資料,因為當我們串好room與hotel之後,我們只要回傳room就可以回朔他是哪一個飯店的房間資料等等的,但這資料可以等我們Day25整個大收尾前,再來練習!


上一篇
「全端挑戰」node.js Api介紹與實作、async function 與try{} catch介紹
下一篇
「全端挑戰」 (req, res, next) next()等多種用法 ,Api串接CRUD的練習與熟悉 part2
系列文
自己做一個價值幾十萬的動態網站,學會Mern開發、前台UI設計各式觀念與各式Lib、typescript你該學會的前端技術30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言