专栏首页性能与架构Express.js 4,Node.js,MongoDB REST API 简易教程

Express.js 4,Node.js,MongoDB REST API 简易教程

教程内容

采用测试驱动开发的方式,开发一个简单的 REST API,包括基本的 POST/GET/PUT/DELETE 操作

先编写好针对各个接口的测试代码,包括:

  1. 调用post接口插入一个对象
  2. 调用get接口获取某个对象的数据
  3. 调用get接口获取集合数据
  4. 调用put接口更新某个对象数据
  5. 调用get接口获取更新后的对象
  6. 调用delete接口删除对象

然后针对第一个测试进行代码编写,写完后执行测试,第一个测试通过后,继续开发下一个,再进行测试,这样迭代进行

测试框架采用 Mocha,WEB框架采用 Express.js 4,数据库使用 MongoDB

前期准备

安装好 Nodejs,Mongodb,配置好npm镜像

我使用的是:nvm 安装 nodejs、Mongodb Docker 镜像、淘宝的cnpm镜像,还安装了 supervisor(检测代码变更,自动加载)

创建项目

创建一个目录 test-rest-api,然后在命令行下进入此目录

安装依赖

npm install mocha --save-dev
npm install expect.js --save-dev 
npm install superagent --save-dev
npm install express --save
npm install mongo --save
npm install mongoskin --save
npm install body-parser --save

新建代码文件

  • express.js - api 代码
  • express.test.js - 测试代码

编写测试

express.test.js 内容:

var superagent = require('superagent')
var expect = require('expect.js')
var mongoskin = require('mongoskin')
var db = mongoskin.db('mongodb://@localhost:27017/test-rest', { safe: true })

describe('express rest api server', function() {

     // 测试执行前清空数据库
    before(function() {
        db.collection('test').remove({})
    });

    var id

    // --- 测试 post 

    it('post object', function(done) {
        superagent.post('http://localhost:3000/collections/test')
            .send({
                name: 'Johns',
                email: 'john@rpjs.co'
            })
            .end(function(e, res) {
                // console.log(res.body)
                expect(e).to.eql(null)
                expect(res.body.ops.length).to.eql(1)
                expect(res.body.ops[0]._id.length).to.eql(24)
                id = res.body.ops[0]._id
                done()
            })
    })

    // --- 测试 get by ID

    it('retrieves an object', function(done) {
        superagent.get('http://localhost:3000/collections/test/' + id)
            .end(function(e, res) {
                // console.log(res.body)
                expect(e).to.eql(null)
                expect(typeof res.body).to.eql('object')
                expect(res.body._id.length).to.eql(24)
                expect(res.body._id).to.eql(id)
                done()
            })
    })
    // --- 测试 get 集合
    
    it('retrieves a collection', function(done) {
        superagent.get('http://localhost:3000/collections/test')
            .end(function(e, res) {
                // console.log(res.body)
                expect(e).to.eql(null)
                expect(res.body.length).to.be.above(0)
                expect(res.body.map(function(item) {
                    return item._id
                })).to.contain(id)
                done()
            })
    })
    // --- 测试 update 更新
    
    it('updates an object', function(done) {
        superagent.put('http://localhost:3000/collections/test/' + id)
            .send({
                name: 'Peter',
                email: 'peter@yahoo.com'
            })
            .end(function(e, res) {
                expect(e).to.eql(null)
                done()
            })
    })
    
    // --- 验证更新后的数据
    
    it('checks an updated object', function(done) {
        superagent.get('http://localhost:3000/collections/test/' + id)
            .end(function(e, res) {
                // console.log(res.body)
                expect(e).to.eql(null)
                expect(typeof res.body).to.eql('object')
                expect(res.body._id.length).to.eql(24)
                expect(res.body._id).to.eql(id)
                expect(res.body.name).to.eql('Peter')
                done()
            })
    })
    
    // --- 测试 remove 删除

    it('removes an object', function(done) {
        superagent.del('http://localhost:3000/collections/test/' + id)
            .end(function(e, res) {
                // console.log(res.body)
                expect(e).to.eql(null)
                expect(res.body.msg).to.eql('success')
                done()
            })
    })
})

运行测试

./node_modules/mocha/bin/mocha express.test.js

运行的结果一定是全部失败,因为还没有编写实际代码,下面就编写代码,使测试一个个的通过。

编写api

express.js 内容:

var express = require('express'),
  mongoskin = require('mongoskin'),
  bodyParser = require('body-parser')  

var app = express()

app.use(bodyParser())

var db = mongoskin.db('mongodb://@localhost:27017/test-rest', {safe:true})

app.param('collectionName', function(req, res, next, collectionName){
  req.collection = db.collection(collectionName)
  return next()
})

app.get('/', function(req, res) {
  res.send('欢迎')
})

// --- 后续功能代码区域

// -----------------

app.listen(3000)

上面是最基础的代码,连接到了数据库,启动了http服务

运行

node express.js

如果安装了 supervisor,就使用它来启动,之后改动 express.js 的话就不用重新启动了

supervisor express.js

启动后使用浏览器访问

http://localhost:3000/

可以看到欢迎信息,基础做好了,开始编写功能代码

添加 post 创建对象

在功能代码区添加:

app.post('/collections/:collectionName', function(req, res, next) {
  req.collection.insert(req.body, {}, function(e, results){
    if (e) return next(e)
    res.send(results)
  })
})

如果没使用 supervisor,记得重新执行 node express.js

执行测试

./node_modules/mocha/bin/mocha express.test.js

测试运行结果:

  express rest api server
    ✓ post object (41ms)
    1) retrieves an object
    2) retrieves a collection
    3) updates an object
    4) checks an updated object
    5) removes an object


  1 passing (81ms)
  5 failing

post 功能通过了

添加 get 根据ID取得对象信息

app.get('/collections/:collectionName/:id', function(req, res, next) {
  req.collection.findById(req.params.id, function(e, result){
    if (e) return next(e)
    res.send(result)
  })
})

重启、测试,结果:

  express rest api server
    ✓ post object (97ms)
    ✓ retrieves an object
    1) retrieves a collection
    2) updates an object
    3) checks an updated object
    4) removes an object


  2 passing (179ms)
  4 failing

添加 get 获取集合数据

app.get('/collections/:collectionName', function(req, res, next) {
  req.collection.find({} ,{limit:10, sort: [['_id',-1]]}).toArray(function(e, results){
    if (e) return next(e)
    res.send(results)
  })
})

重启、测试,结果:

  express rest api server
    ✓ post object (90ms)
    ✓ retrieves an object
    ✓ retrieves a collection
    1) updates an object
    2) checks an updated object
    3) removes an object


  3 passing (179ms)
  3 failing

添加 update 根据ID修改对象数据

app.put('/collections/:collectionName/:id', function(req, res, next) {
  req.collection.updateById(req.params.id, {$set:req.body}, function(e, result){
    if (e) return next(e)
    res.send(result)
  })
})

重启、测试,结果:

  express rest api server
    ✓ post object (128ms)
    ✓ retrieves an object
    ✓ retrieves a collection
    ✓ updates an object
    ✓ checks an updated object
    1) removes an object


  5 passing (230ms)
  1 failing

添加 delete 根据ID删除对象

app.del('/collections/:collectionName/:id', function(req, res, next) {
  req.collection.removeById(req.params.id, function(e, result){
    if (e) return next(e)
    res.send((result===1)?{msg:'success'}:{msg:'error'})
  })
})

重启、测试,结果:

  express rest api server
    ✓ post object (40ms)
    ✓ retrieves an object
    ✓ retrieves a collection
    ✓ updates an object
    ✓ checks an updated object
    ✓ removes an object


  6 passing (103ms)

这样,所有测试都就通过了,代码开发完成

小结

通过这个小例子,可以了解nodejs express的开发方式,并体验了测试驱动的开发方法

如果您感觉在公众号文章中不方便练习,可获取PDF版本

本文分享自微信公众号 - 性能与架构(yogoup),作者:杜亦舒

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-01-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • mysql 大数据量的分页优化

    使用limit进行分页,翻到10000多页后效率低 原因 limit offset 会逐行查找,是先查询再跳过 优化思路 (1)从业务逻辑 不允许翻过100页,...

    dys
  • 8个你会爱上的 VS Code 扩展

    https://marketplace.visualstudio.com/items?itemName=aaron-bond.better-comments

    dys
  • MySQL 清除表空间碎片

    表的存储会出现碎片化,每当删除了一行内容,该段空间就会变为空白 当执行插入操作时,MySQL会尝试使用空白空间,但如果某个空白空间一直没有被大小合适的数据占用...

    dys
  • Go 脚本往InfluxDB插入数据

    简单、
  • Django之CURD插件

    CURD顾名思义就是create,update,rearch,delete(所谓的增删改查).

    菲宇
  • 打通工业物联网的“最后一公里”, 构建顶级SMT贴片厂!

    麦肯锡研究院最新预测,到2025年工业4.0模式带来的经济影响价值将达每年1.2万亿美元至3.7万亿美元。埃森哲联合Frontier Economics预估了工...

    万物皆可联i
  • 【leetcode】背包问题

    完全背包的特点恰是每种物品可选无限件,所以就可以并且必须采用 w = W[i]…carry 的顺序循环:

    JNingWei
  • Android P 中的新文本特性

    在 “What’s new in Android P Beta” 中我们已经谈到 Android 的两个新文本特性。现在既然 Android P Beta 3 ...

    Android 开发者
  • Unity3D关于Text方面的类

    TextAlignment 多行文本应该如何被对齐 这个是被GUIText.alignment属性使用 参见:GUI Text component 值 L...

    py3study

扫码关注云+社区

领取腾讯云代金券