1. 程式人生 > 其它 >一個簡單的Koa電影評分系統

一個簡單的Koa電影評分系統

基本需求

這個文章想用Koa寫一個比較簡單的、具有基本CRUD功能的電影評分系統。大概提供幾個介面:返回所有電影資訊、電影資訊詳情、修改電影詳情和刪除某電影詳情的功能。

建立一個基本服務

上一篇文章講了,Koa基本就是對Node.js的http包做了一下包裝,讓建立服務這件事比較方便了。但對一些像路由、解析POST請求等需求還是需要一些額外的工具的。先來建立一個新專案:

mkdir movie-api && cd movie-api
npm init -y
npm i koa @koa/router koa-bodyparser mysql2 -S

新建app.js

const Koa = require('koa')
const Router = require('@koa/router')
const router = new Router()
const bodyParser = require('koa-bodyparser')
const app = new Koa()

router.get('/', ctx => {
  ctx.body = 'server ok'
})

app.use(bodyParser())
  .use(router.routes())
  .listen(3000, () => console.log('server is running at 3000'))

執行一下這個檔案,就得到了一個基本的Koa服務。

加上資料庫

這次要用從真的資料庫裡取資料返給請求處,所以建立一個數據庫movie_db,新增一些資料:

INSERT INTO `movie` (`id`, `name`, `year`) VALUES
(1, '星際穿越', '2014'),
(2, '大腕', '2001'),
(3, '飛馳人生', '2019'),
(4, '成為詹姆斯·邦德:丹尼爾·克雷格的故事', '2021'),
(5, '空前絕後滿天飛', '1980'),
(6, '007:大破天幕殺機', '2012'),
(7, '黑客帝國', '1999'),
(8, '哈利·波特與魔法石', '2001'),
(9, '流浪地球', '2019'),
(10, '瘋狂動物城', '2016'),
(11, '驚異大奇航', '1987'),
(12, '愛樂之城', '2016'),
(13, '諜影重重', '2002'),
(14, '灰獵犬號', '2020'),
(15, '復仇者聯盟4:終局之戰', '2019'),
(16, '小森林冬春篇', '2015');

之後,在專案裡新建一個db.js

const mysql = require('mysql2/promise')

const connection = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: 'root',
  database: 'movie_db'
})

module.exports = connection

然後在app.js中修改程式碼:

const Koa = require('koa')
const Router = require('@koa/router')
const router = new Router()
const bodyParser = require('koa-bodyparser')
const app = new Koa()
+ const connection = require('./db')

router.get('/', ctx => {
  ctx.body = 'server ok'
})

+ router.get('/movie', async ctx => {
+   const res = await connection.then(conn => conn.query('SELECT * FROM movie'))
+   ctx.body = JSON.stringify(res[0])
+ })

app.use(bodyParser())
  .use(router.routes())
  .listen(3000, () => console.log('server is running at 3000'))

這裡要說明的是,mysql2中實現了Promise wrapper,在庫層面上支援了Promise的呼叫方式。所以這裡在db.js裡匯出的只是一個Promise,所以在這裡是不能直接connection.query()這種同步呼叫方式的,只能用.then(conn => {})這種形式呼叫。query()結束後得到的結果裡有兩個物件,第一個是我們要找的資料,而第二個暫時還不知道是什麼,所以這裡直接返回res[0]

以此類推,我們可以寫出其他介面:

router.post('/movie', async ctx => {
  const body = ctx.request.body
  const res = await connection.then(conn => conn.query('INSERT INTO movie (name, year) VALUES (?, ?)'
    , [body.name, body.year]))
    .catch(err => console.log(err))
  ctx.body = res
})

router.put('/movie/:id', async ctx => {
  const id = ctx.params.id
  const year = ctx.request.body.year
  const res = await connection.then(conn => conn.query('UPDATE movie SET year=? WHERE id=?'
    , [year, id]))
    .catch(err => console.log(err))
  ctx.body = res
})

router.del('/movie/:id', async ctx => {
  const id = ctx.params.id
  const res = await connection.then(conn => conn.query('DELETE FROM movie WHERE id=?', [id]))
  ctx.body = res
})

題外話

為了程式碼格式一致,我在這裡用了一個standard的工具修復程式碼格式,要啟用這個工具,需要額外加一條:

npm i standard -D

儲存所有檔案,在專案資料夾下執行standard --fix,即可自動將程式碼格式變成standard強制的樣式