JavaScript >> Javascript 文檔 >  >> Node.js

教程:Node.js 和 MongoDB JSON REST API 服務器與 Mongoskin 和 Express.js

更新3: 本教程的 Expess 4 版本可在 Express.js 4、Node.js 和 MongoDB REST API 教程以及 github.com/azat-co/rest-api-express(master 分支)上獲得。本教程適用於 Express 3.x。

更新2 :“Mongoskin 刪除了‘db.collection.id’並添加了一些 actionById 方法”從此拉取請求中更改了此代碼。要使用本文中的代碼,只需安裝舊版本的 Mongoskin(0.5.0?)。 GitHub 中的代碼將適用於 Mongoskin 1.3.20。

更新2 :“Mongoskin 刪除了‘db.collection.id’並添加了一些 actionById 方法”從此拉取請求中更改了此代碼。要使用本文中的代碼,只需安裝舊版本的 Mongoskin(0.5.0?)

更新 :使用來自此存儲庫 github.com/azat-co/rest-api-express(express3 分支)的增強代碼。

注意: 本文是 Express.js 指南的一部分:Express.js 綜合書籍。

本教程將引導您使用 Mocha 和 Super Agent 庫編寫測試,然後以測試驅動的開發方式使用它們,利用 Express.js 框架和用於 MongoDB 的 Mongoskin 庫構建一個 Node.js 免費的 JSON REST API 服務器。在這個 REST API 服務器中,我們將執行創建、讀取、更新和刪除 (CRUD) 操作和利用 app.param() 的 Express.js 中間件概念 和 app.use() 方法。

測試覆蓋率

首先讓我們編寫功能測試,向我們即將創建的 REST API 服務器發出 HTTP 請求。如果您知道如何使用 Mocha 或者只是想直接跳轉到 Express.js 應用程序實現,請隨意這樣做。您也可以使用 CURL 終端命令進行測試。

假設我們已經安裝了 Node.js、NPM 和 MongoDB,讓我們創建一個 new 文件夾(或者如果您編寫測試使用該文件夾):

mkdir rest-api
cd rest-api

我們將使用 Mocha、Expect.js 和 Super Agent 庫。要安裝它們,請從項目文件夾運行以下命令:

$ npm install [email protected] --save-dev
$ npm install [email protected] --save-dev 
$ npm install [email protected] --save-dev

現在讓我們創建 express.test.js 同一文件夾中的文件將有六個套件:

  • 創建一個新對象
  • 通過 ID 檢索對象
  • 檢索整個集合
  • 通過 ID 更新對象
  • 通過 ID 檢查更新的對象
  • 按 ID 移除對象

使用 Super Agent 的鍊式函數,HTTP 請求變得輕而易舉,我們將把它們放在每個測試套件中。這是 express.test.js 的完整源代碼 文件:

var superagent = require('superagent')
var expect = require('expect.js')

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

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

  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()
      })
  })

  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()
      })
  })

  it('updates an object', function(done){
    superagent.put('http://localhost:3000/collections/test/'+id)
      .send({name: 'Peter'
        , email: '[email protected]'})
      .end(function(e, res){
        // console.log(res.body)
        expect(e).to.eql(null)
        expect(typeof res.body).to.eql('object')
        expect(res.body.msg).to.eql('success')        
        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()
      })
  })    
  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(typeof res.body).to.eql('object')
        expect(res.body.msg).to.eql('success')    
        done()
      })
  })      
})

要運行測試,我們可以使用 $ mocha express.test.js 命令。

依賴關係

在本教程中,我們將使用 Mongoskin,這是一個 MongoDB 庫,它是用於 Node.js 的普通舊原生 MongoDB 驅動程序的更好替代品。此外,Mongoskin 比 Mongoose 更輕量級且無模式。如需更多信息,請查看 Mongoskin 比較簡介。

Express.js 是核心 Node.js HTTP 模塊對象的包裝器。 Express.js 框架建立在 Connect 中間件之​​上,並提供了大量的便利。有些人將框架與 Ruby 的 Sinatra 進行比較,因為它是非主觀的和可配置的。

[旁注]

閱讀博客文章很好,但觀看視頻課程更好,因為它們更具吸引力。

許多開發人員抱怨 Node.js 上缺乏負擔得起的高質量視頻材料。觀看 YouTube 視頻會讓人分心,花 500 美元購買 Node 視頻課程很瘋狂!

去看看 Node University,它有關於 Node 的免費視頻課程:node.university。

[旁注結束]

如果您創建了 rest-api 上一節中的文件夾測試覆蓋率 ,只需運行以下命令即可為應用程序安裝模塊:

npm install [email protected] --save
npm install [email protected] --save

實施

首先,讓我們定義我們的依賴項:

var express = require('express')
  , mongoskin = require('mongoskin')

在 3.x 版本之後,Express 簡化了其應用實例的實例化,以這種方式將給我們一個服務器對象:

var app = express()

要從請求正文中提取參數,我們將使用 bodyParser() 看起來更像配置語句的中間件:

app.use(express.bodyParser())

中間件(以這種形式和其他形式)是 Express.js 和 Connect 中用於組織和重用代碼的強大且方便的模式。

bodyParser() 一樣 Mongoskin 使我們免於解析 HTTP 請求的主體對象的障礙,可以通過一行代碼輕鬆連接到 MongoDB 數據庫:

var db = mongoskin.db('localhost:27017/test', {safe:true});

注意:如果您希望連接到遠程數據庫,例如 MongoHQ 實例,請將字符串替換為您的用戶名、密碼、主機和端口值。這是 URI 字符串的格式:mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]

app.param() 方法是另一個 Express.js 中間件。它基本上說“每次在請求處理程序的 URL 模式中有這個值時做一些事情 ”。在我們的例子中,當請求模式包含一個字符串 collectionName 時,我們選擇一個特定的集合 以冒號為前綴(您將在後面的路線中看到它):

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

只是為了用戶友好,讓我們在根路由中添加一條消息:

app.get('/', function(req, res) {
  res.send('please select a collection, e.g., /collections/messages')
})

現在真正的工作開始了,下面是我們如何檢索按 _id 排序的項目列表 並且限制為 10:

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

你有沒有註意到一個 :collectionName URL 模式參數中的字符串?這個和之前的 app.param() 中間件為我們提供了 req.collection 指向我們數據庫中指定集合的​​對象。

對象創建端點稍微容易掌握,因為我們只是將整個有效負載傳遞給 MongoDB(方法又名免費 JSON REST API):

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

單個對象檢索函數比 find() 更快 ,但它們使用不同的接口(它們直接返回對象而不是光標),所以請注意這一點。此外,我們從 :id 中提取 ID req.params.id 的部分路徑 Express.js 的魔力:

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

PUT 請求處理程序變得更有趣,因為 update() 不返回增強對象,而是返回我們受影響對象的計數。

還有 {$set:req.body} 是一個特殊的 MongoDB 運算符(運算符通常以美元符號開頭),用於設置值。

第二個{safe:true, multi:false} 參數是一個帶有選項的對象,告訴 MongoDB 在運行回調函數之前等待執行,並且只處理一個(第一個)項目。

app.put('/collections/:collectionName/:id', function(req, res) {
  req.collection.update({_id: req.collection.id(req.params.id)}, {$set:req.body}, {safe:true, multi:false}, function(e, result){
    if (e) return next(e)
    res.send((result===1)?{msg:'success'}:{msg:'error'})
  })
})

最後,同樣輸出自定義 JSON 消息的 DELETE 方法:

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

注意:delete 是 JavaScript 中的運算符,因此 Express.js 使用 app.del 而是。

在這種情況下,實際上在端口 3000 上啟動服務器的最後一行:

app.listen(3000)

以防萬一有些事情不是很好,這裡是 express.js 的完整代碼 文件:

var express = require('express')
  , mongoskin = require('mongoskin')

var app = express()
app.use(express.bodyParser())

var db = mongoskin.db('localhost:27017/test', {safe:true});

app.param('collectionName', function(req, res, next, collectionName){
  req.collection = db.collection(collectionName)
  return next()
})
app.get('/', function(req, res) {
  res.send('please select a collection, e.g., /collections/messages')
})

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

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

app.get('/collections/:collectionName/:id', function(req, res) {
  req.collection.findOne({_id: req.collection.id(req.params.id)}, function(e, result){
    if (e) return next(e)
    res.send(result)
  })
})
app.put('/collections/:collectionName/:id', function(req, res) {
  req.collection.update({_id: req.collection.id(req.params.id)}, {$set:req.body}, {safe:true, multi:false}, function(e, result){
    if (e) return next(e)
    res.send((result===1)?{msg:'success'}:{msg:'error'})
  })
})
app.del('/collections/:collectionName/:id', function(req, res) {
  req.collection.remove({_id: req.collection.id(req.params.id)}, function(e, result){
    if (e) return next(e)
    res.send((result===1)?{msg:'success'}:{msg:'error'})
  })
})

app.listen(3000)

退出編輯器並在終端中運行它:

$ node express.js

在另一個窗口中(不關閉第一個窗口):

$ mocha express.test.js

如果你真的不喜歡 Mocha 和/或 BDD,CURL 總是在你身邊。 :-)

例如 CURL 數據發出 POST 請求:

$ curl -d "" http://localhost:3000

GET 請求也可以在瀏覽器中使用,例如 http://localhost:3000/test。

在本教程中,我們的測試比應用程序代碼本身要長,因此放棄測試驅動開發可能很誘人,但請相信我TDD 的好習慣將為您節省大量時間 在任何嚴肅的開發過程中,當您使用的應用程序的複雜性很大時。

結論

當您需要用幾行代碼構建一個簡單的 REST API 服務器時,Express.js 和 Mongoskin 庫非常有用。稍後,如果您需要擴展這些庫,它們還提供了一種配置和組織代碼的方法。

像 MongoDB 這樣的 NoSQL 數據庫擅長免費的 REST API,我們不必定義模式,可以拋出任何數據,並且會被保存。

測試和應用文件的完整代碼:https://gist.github.com/azat-co/6075685。

如果您想了解有關 Express.js 和其他 JavaScript 庫的更多信息,請查看 Express.js 簡介系列教程。

注意 :在這個例子中,我使用了少分號的樣式。 JavaScript 中的分號是絕對可選的,除了以下兩種情況:在 for 循環中和以括號開頭的表達式/語句之前(例如,立即調用函數表達式)。


Tutorial JavaScript 教程
  1. 習慣使用 Google。

  2. 讓我們來解決 LeetCode!二和

  3. 將 Webiny Headless CMS 與我們用於 Gatsby 和 NextJS 的新入門工具包一起使用

  4. 如何在 JavaScript 中檢查字符串是否不以正則表達式開頭

  5. 🎨 調色板發布!

  6. React 和組件,axios

  7. 在 React 中加載屏幕的不同方法

  1. 現代 Web 開發設置 #1

  2. 使用 JavaScript 動態更改網站圖標

  3. 刪除線文本 Twitter:以粗體、斜體和刪除線文本發布推文

  4. 我們如何去抖動渲染一個 React 組件? (舉例)

  5. 使用 Nextjs 和 ChakraUI 創建專業的作品集網站

  6. 使用 Esbuild 捆綁 Figma 插件

  7. 如何在 Angular 中創建自定義管道

  1. 樣式化組件的生產力

  2. 介紹 Angular 組件測試

  3. 如何在 10 分鐘內創建一個免費的開發者博客?

  4. 使用 Chakra UI 截斷文本