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)

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

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏JackeyGao的博客

一个超级小的 Django 项目.

当用最简单的代码实现 Django 项目为最基本的要素的时候, 项目可以和微框架一样小.

2112
来自专栏idba

ZanDB基于Celery定时任务的二次开发

ZanDB早期的任务需求中,大部分都是针对servant(跑在主机上的agent)做任务调度。也就是说,一期的任务系统,满足的是在特定时刻调用特定主机执行特定的...

1542
来自专栏草根专栏

用 Identity Server 4 (JWKS 端点和 RS256 算法) 来保护 Python web api

[新添加] 本文对应的源码 (多个flow, clients, 调用python api): https://github.com/solenovex/Iden...

4028
来自专栏Greenplum

Linux 常用命令(二)

Linux是一套免费使用和自由传播的类Unix操作系统(主要用在服务器上),接下来详细的介绍一下linux的一些知识。

3220
来自专栏Linux驱动

makefile使用.lds链接脚本以及 $@ ,$^, $,< 解析

先来分析一个简单的.lds链接脚本 例1,假如现在有head.c init.c nand.c main.c这4个文件: 1.1 首先创建链接脚本nand.lds...

29710
来自专栏图形学与OpenGL

升级硬盘重装Win10系统总结

1482
来自专栏云计算

腾讯云支持 Terraform 开发实践

这篇文章从系统架构开始,到核心库讲解,到实践开发,再到单元测试,比较完整的描述了支持Terraform的开发全过程。

4.6K18
来自专栏owent

Linux 编译安装 GCC 4.9

GCC4.9发布啦,本脚本在之前4.8的基础上做了稍许改进,更新 PS:4.9.0 开始支持C++1y特性 GCC 4.9 的大致变更如下,因为我只用C/C...

4821
来自专栏雪胖纸的玩蛇日常

1.Ubuntu系统与vmware虚拟机的安装与使用

2035
来自专栏xingoo, 一个梦想做发明家的程序员

汇编语言 手记3

从读写属性上存储器分为:随机存储器RAM和只读存储器ROM 从功能和连接上分类: 随机存储器RAM 装有BIOS的ROM 接口卡上的RAM ? 上述的存储器物理...

22510

扫码关注云+社区

领取腾讯云代金券