Rollup+Restify+typescript+mongodb開發後端http服務

背景:

向小程序,提供http/https接口服務,存儲用戶數據、日誌數據。

技術選型

resitify

純粹的http服務,不涉及視圖

typescript 2.1+

類型校驗,規範代碼

mock.js

提供mock服務

mongodb

文檔結構,存儲數據

rollup

js管理

node

後端服務管理

pm2

後端node進程管理

環境搭建

安裝node

默認已安裝node,當前使用版本v10.16.3。

初始化工程

npm init -y

//得到如下package.json:

{
"name": "restify-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"

安裝依賴

//產線環境使用
npm install --save mongodb restify mockjs pm2
//開發環境使用
## 安裝 rollup.js 基礎模塊
npm i --save-dev rollup rollup-plugin-buble

## 安裝 rollup.js 編譯代碼混淆插件
npm i --save-dev rollup-plugin-uglify

## 安裝 rollup.js 編譯 Typescript 代碼的插件模塊
npm i --save-dev rollup-plugin-typescript typescript tslib

安裝後package.json

{
"name": "restify-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "rollup -c rollup.config.js",
"start":"node dist/app.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"mockjs": "^1.1.0",
"mongodb": "^3.3.4",
"pm2": "^4.1.2",
"restify": "^8.4.0"

},
"devDependencies": {
"rollup": "^1.27.2",
"rollup-plugin-buble": "^0.19.8",
"rollup-plugin-typescript": "^1.0.1",
"rollup-plugin-uglify": "^6.0.3",
"tslib": "^1.10.0",
"typescript": "^3.7.2"
}
}

配置rollup

touch rollup.config.js 

const path = require('path');
const buble = require('rollup-plugin-buble');
const typescript = require('rollup-plugin-typescript');

const resolveFile = function(filePath) {
return path.join(__dirname, filePath)
}

module.exports = [
{
input: resolveFile('src/main.ts'),
output: {
file: resolveFile('dist/app.js'),
format: 'cjs',
name: 'restify-demo',
},
plugins: [
typescript(),
buble(),
],
},
]

安裝mongodb

下載mongo

# 進入 /usr/local
cd /usr/local

# 下載
sudo curl -O https://fastdl.mongodb.org/osx/mongodb-osx-ssl-x86_64-4.0.9.tgz

# 解壓
sudo tar -zxvf mongodb-osx-ssl-x86_64-4.0.9.tgz

# 重命名為 mongodb 目錄

sudo mv mongodb-osx-x86_64-4.0.9/ mongodb

export PATH=/usr/local/mongodb/bin:$PATH

添加mongo.conf

# 日誌
systemLog:
# 日誌為文件
destination: file
# 文件位置
path: /usr/local/var/log/mongodb/mongo.log
# 是否追加
logAppend: true
#進程
processManagement:
# 守護進程方式
fork: true
storage:
dbPath: /usr/local/var/mongodb
net:
# 綁定IP,默認127.0.0.1,只能本機訪問
bindIp: 127.0.0.1
# 端口
port: 27017

運行mongod

mongod --config /usr/local/etc/mongo.conf

校驗mongo

> mongo 
MongoDB shell version v4.0.9
connecting to: mongodb://127.0.0.1:27017/?gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("07aaf4a0-c188-4d50-9412-96a2b0625e8a") }
MongoDB server version: 4.0.9
Server has startup warnings:
2019-11-27T17:31:59.406+0800 I CONTROL [initandlisten]
2019-11-27T17:31:59.407+0800 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database.

2019-11-27T17:31:59.408+0800 I CONTROL [initandlisten] **

hello-world

import restify from "restify";
import { Server } from "restify/lib/server";

function respond(req, res, next) {
res.send('hello ' + req.params.name);
next();
}

const server:Server = restify.createServer();
server.get('/hello/:name', respond);
server.head('/hello/:name', respond);

server.listen(8090, function() {
console.log('%s listening at %s', server.name, server.url);
});

hello-world請求

curl -is http://localhost:8090/hello/world -H 'accept: text/plain'
## 返回
'accept: text/plain'
HTTP/1.1 200 OK
Server: restify
Content-Type: text/plain
Content-Length: 11
Date: Wed, 20 Nov 2019 05:35:12 GMT
Connection: keep-alive

hello world

業務實踐

依賴引入

import restify from "restify";
import { Server } from "restify/lib/server";
import Mock from 'mockjs'
import { dbConnect } from "./utils";
const mongodb = require('mongodb');
const log4js = require('log4js');

const logger = log4js.getLogger();
const server:Server = restify.createServer();
//before route choose

server.pre(restify.plugins.pre.userAgentConnection());
//after router choose ,before handler
server.use(restify.plugins.acceptParser(server.acceptable));
server.use(restify.plugins.queryParser({ mapParams: false }));
server.use(restify.plugins.bodyParser());

restify 路由配置

//set route 
server.post('/add',
async function(req, res, next) {
const desc = req.body.desc;
let result = await create(desc);
req.desc = {desc:'success',data:{
desc:result,
numbrer:Mock.mock({
"number|1-100": 100
})
}};
return next();
},
function(req, res, next) {
res.send(req.desc);
return next();
}
);
server.get(
'/all',
async function(req, res, next) {
let result = await fetchAll();
req.desc = result;
return next();
},
function(req, res, next) {
res.send(req.desc);
return next();
}
)

api版本支持

function sendV1(req, res, next) {
const mockNumber = Mock.mock({
"number|1-100": 100
});
res.send(`version number:${mockNumber} with param ${req.params.name}`);
return next();
}

function sendV2(req, res, next) {

const mockNumber = Mock.mock({
"number|1-100": 100
});
res.send({ param: req.params.name,version: mockNumber});
return next();
}

server.get('/version/:name', restify.plugins.conditionalHandler([
{ version: '1.1.3', handler: sendV1 },
{ version: ['2.0.0', '2.1.0', '2.2.0'], handler: sendV2 }
]));

mongo 操作

let db;

// Get a DB connection when this module is loaded
(function getDbConnection() {
dbConnect().then((database) => {
db = database;
}).catch((err) => {
logger.error('Error while initializing DB: ' + err.message, 'lists-dao-mongogb.getDbConnection()');
});
})();

function create(description) {
return new Promise((resolve, reject) => {
let lists = db.collection('shoppingLists');
let listId = mongodb.ObjectId();
let whenCreated = Date.now();
let item = {
_id: listId,
id: listId,
description: description,
whenCreated: whenCreated,
whenUpdated: null
};
lists.insertOne(item, (err, result) => {
if (err) {
logger.error('Error occurred: ' + err.message, 'create()');
reject(err);
} else {
resolve({ data: { createdId: result.insertedId }, statusCode: 201 });
}
});
});
}

function fetchAll() {
return new Promise((resolve, reject) => {

let lists = db.collection('shoppingLists');
lists.find({}).toArray((err, documents) => {
if (err) {
logger.error('Error occurred: ' + err.message, 'fetchAll()');
reject(err);
} else {
logger.debug('Raw data: ' + JSON.stringify(documents), 'fetchAll()');
resolve({ data: JSON.stringify(documents), statusCode: (documents.length > 0) ? 200 : 404 });
}
});
});
}

db連接

const mongodb = require('mongodb');
const log4js = require('log4js');
const logger = log4js.getLogger();
// logger.level = 'debug';

let mongodbClient;
let db;

const appSettings = {
mongodb_url:'mongodb://127.0.0.1:27017/?gssapiServiceName=mongodb',
mongodb_db_name:'mongoDemo',
}
function dbClose() {
if (mongodbClient && mongodbClient.isConnected()) {
mongodbClient.close();
}
}

export function dbConnect() {
return new Promise((resolve, reject) => {
if (db) {
resolve(db);
} else {
mongodb.MongoClient.connect(appSettings.mongodb_url, function(err, client) {
if (err) {
logger.error('Error connecting to the MongoDB URL: ' + appSettings.mongodb_url);
reject(err);
}
mongodbClient = client;
db = mongodbClient.db(appSettings.mongodb_db_name);
// Make sure connection closes when Node exits
process.on('exit', (code) => {
dbClose();
})

resolve(db);
});
}
});
}

參考文獻

  • restify.com/docs/home/
  • mongodb.github.io/node-mongodb-native/
  • www.rollupjs.com/guide/introduction/
  • github.com/nuysoft/Mock/wiki/Getting-Started
  • github.com/rollup/rollup-plugin-typescript
  • typescript.bootcss.com/
  • www.runoob.com/mongodb/mongodb-osx-install.html
  • www.jianshu.com/p/ec50c1088cf1

本文作者:前端首席體驗師(CheongHu)

聯繫郵箱:[email protected]


分享到:


相關文章: