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

mongodb

rollup

mockJs

typescript plugin

typescript

菜鸟教程mongodb安装

配置mongodb

本文作者:前端首席体验师(CheongHu)
联系邮箱:simple2012hcz@126.com
版权声明: 本文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容