以往因為javascript的許多殘缺設計,使得我轉身投靠iced-coffeescript(進階版coffeescript)。老實說我用的很開心,在ES5年代,當眾多碼農在callback hall裡苦苦掙扎之時,我已經愉快地使用iced-coffeescript的async/await,輕鬆解決callback hell跟ES6的Promise.all
功能。
但隨著時代的演進,javascript語言本身也進化到了ES7,源自coffeescript的胖箭頭跟async也被寫進標準了,加上我受到一些啟發,我想是時候重新擁抱JS,並且好好整理一番我常用的NodeJS+Express+ES7+MySQL起手架。
首先,如果要使用async,NodeJS必須升級到7.0以上,過程一點都不可怕,到官網下載安裝檔然後下一步、下一步、下一步就完成了。
此外,我還是不打算用ORM,因為只要稍微複雜一點的query,SQL還是完勝ORM
把node-mysql改成async/await
原本node-mysql的最佳實踐是用創造一個連線池(pool
),把connection的生命週期交給pool
去管理/回收,標準用法如下
pool.query('SELECT * FROM User WHERE id=?',[1],(err,result) => {
if(err) return errorHandler(res,err);
...
});
由於async是建構在promise之上,而node-mysql官方似乎沒有提供promise實作,所以我們必須自己包:
// db.js
const db = {
query (query,params) {
return new Promise(resolve, reject) {
pool.query(query,params,(err, result) => {
if(err) reject(err);
else resolve(result);
});
}
}
}
export default db;
正由於promise會throw exception,使用時也必須用try catch包住
router.get('/user/:id', async (req,res) => {
try {
const user = await db.query('SELECT * FROM User WHERE id=?',[req.params.id])
const notebook = await db.query('...', otherParams)
}
catch (e) {
return errorHandler(res,err)
}
});
如果你覺得每次都要try catch太瑣碎,這裡有解決方案A、解決方案B、解決方案C,注意的是,當你有多個query時,用try-catch
還是比較省心。
require進化成import
很不幸地,NodeJS 7.0還是不支援import,必須用babel轉譯,而且整個app啟動時必須先require('babel-register')
,最佳實踐如下
- 修改package.json,然後
npm install
"scripts": {
//假設用node-dev
"dev": "node-dev --use_strict ./bin/index"
},
"devDependencies": {
"MD5": "~1.2.0",
"babel-cli": "^6.24.1",
"babel-preset-es2015-node4": "^2.1.0",
"babel-preset-stage-3": "^6.5.0",
"babel-register": "^6.6.5",
...
}
- 新增.babelrc
{
"presets": ["es2015-node4", "stage-3"]
}
- 新增./bin/index.js
require('babel-register');
require('../app.js');
npm run dev
整理router
未完待續