Contribute on GitHub

Getting started


package.json

{
  "dependencies": {
    "express-restify-mongoose": "^2.0.0",
    "mongoose": "^4.0.0"
  }
}

From the command line

npm install express-restify-mongoose --save

Express 4 app

This snippet…

var express = require('express')
var bodyParser = require('body-parser')
var methodOverride = require('method-override')
var mongoose = require('mongoose')
var restify = require('express-restify-mongoose')
var app = express()
var router = express.Router()

app.use(bodyParser.json())
app.use(methodOverride())

mongoose.connect('mongodb://localhost/database')

restify.serve(router, mongoose.model('Customer', new mongoose.Schema({
  name: { type: String, required: true },
  comment: { type: String }
})))

app.use(router)

app.listen(3000, function () {
  console.log('Express server listening on port 3000')
})

…automatically generates those endpoints.

GET http://localhost/api/v1/Customers/count
GET http://localhost/api/v1/Customers
PUT http://localhost/api/v1/Customers
POST http://localhost/api/v1/Customers
DELETE http://localhost/api/v1/Customers

GET http://localhost/api/v1/Customers/:id
GET http://localhost/api/v1/Customers/:id/shallow
PUT http://localhost/api/v1/Customers/:id
POST http://localhost/api/v1/Customers/:id
DELETE http://localhost/api/v1/Customers/:id

Usage with request

var request = require('request')

request({
  url: '/api/v1/Model',
  qs: {
    query: JSON.stringify({
      $or: [{
        name: '~Another'
      }, {
        $and: [{
          name: '~Product'
        }, {
          price: '<=10'
        }]
      }],
      price: 20
    })
  }
})

Querying


All the following parameters (sort, skip, limit, query, populate, select and distinct) support the entire mongoose feature set.

When passing values as objects or arrays in URLs, they must be valid JSON

Sort

GET /Customers?sort=name
GET /Customers?sort=-name
GET /Customers?sort={"name":1}
GET /Customers?sort={"name":-1}

Skip

GET /Customers?skip=10

Limit

Only overrides options.limit if the queried limit is lower

GET /Customers?limit=10

Query

Supports all operators ($regex, $gt, $gte, $lt, $lte, $ne, etc.) as well as shorthands: ~, >, >=, <, <=, !=

GET /Customers?query={"name":"Bob"}
GET /Customers?query={"name":{"$regex":"^(Bob)"}}
GET /Customers?query={"name":"~^(Bob)"}
GET /Customers?query={"age":{"$gt":12}}
GET /Customers?query={"age":">12"}
GET /Customers?query={"age":{"$gte":12}}
GET /Customers?query={"age":">=12"}
GET /Customers?query={"age":{"$lt":12}}
GET /Customers?query={"age":"<12"}
GET /Customers?query={"age":{"$lte":12}}
GET /Customers?query={"age":"<=12"}
GET /Customers?query={"age":{"$ne":12}}
GET /Customers?query={"age":"!=12"}

Populate

Works with create, read and update operations

GET/POST/PUT /Invoices?populate=customer
GET/POST/PUT /Invoices?populate={"path":"customer"}
GET/POST/PUT /Invoices?populate=[{"path":"customer"},{"path":"products"}]

Select

_id is always returned unless explicitely excluded

GET /Customers?select=name
GET /Customers?select=-name
GET /Customers?select={"name":1}
GET /Customers?select={"name":0}

Distinct

GET /Customers?distinct=name

Reference


serve

restify.serve(router, model[, options])

router: express.Router() instance (Express 4), app object (Express 3) or server object (restify)

model: mongoose model

options: object typedefaultversion

prefix

string/api

Path to prefix to the REST endpoint

version

string/v1

API version that will be prefixed to the rest path. If prefix or version contains /:id, then that will be used as the location to search for the id

Example

Generates /api/v1/Entities/:id/Model and /api/v1/Entities/Model for all pertinent methods

version: '/v1/Entities/:id'

idProperty

string_id

findById will query on the given property

restify

booleanfalse

Enable support for restify instead of express

plural

booleantrue

Automatically pluralize model names using inflection

lowercase

booleanfalse

Whether to call .toLowerCase() on model names before generating the routes

name

stringmodel name

Endpoint name

readPreference

stringprimary2.3

Determines the MongoDB nodes from which to read. Read more

totalCountHeader

boolean|stringfalse2.4

When set to true, executes a count query on GET /Model requests that sets limit and skip to 0 and sets the result in the X-Total-Count header. It can also be set to a string to allow for a custom header.

Examples

Boolean

totalCountHeader: true

Response:

Headers: {
  'X-Total-Count': 5
}

String

totalCountHeader: 'X-Custom-Count-Header'

Response:

Headers: {
  'X-Custom-Count-Header': 5
}

private

array

Array of fields which are only to be returned by queries that have private access

Example

Defined in options

private: ['topSecret', 'fields']

Defined in mongoose schema

new Schema({
  topSecret: { type: String, access: 'protected' },
  fields: { type: String, access: 'protected' }
})

protected

array

Array of fields which are only to be returned by queries that have private or protected access

Examples

Defined in options

protected: ['somewhatSecret', 'keys']

Defined in mongoose schema

new Schema({
  somewhatSecret: { type: String, access: 'protected' },
  keys: { type: String, access: 'protected' }
})

lean

booleantrue

Whether or not mongoose should use .lean() to convert results to plain old JavaScript objects. This is bad for performance, but allows returning virtuals, getters and setters.

findOneAndUpdate

booleantrue

Whether to use findOneAndUpdate or first findById and then save, allowing document middleware to be called. For more information regarding mongoose middleware, read the docs.

findOneAndRemove

booleantrue

Whether to use findOneAndRemove or first findById and then remove, allowing document middleware to be called. For more information regarding mongoose middleware, read the docs.

preMiddleware

function (req, res, next)2.0

Middleware that runs before preCreate, preRead, preUpdate and preDelete.

Example
preMiddleware: function (req, res, next) {
  performAsyncLogic(function (err) {
    next(err)
  })
}

preCreate

function (req, res, next)2.1

Middleware that runs before creating a resource

preCreate: function (req, res, next) {
  performAsyncLogic(function (err) {
    next(err)
  })
}

preRead

function (req, res, next)2.1

Middleware that runs before reading a resource

preRead: function (req, res, next) {
  performAsyncLogic(function (err) {
    next(err)
  })
}

preUpdate

function (req, res, next)2.1

Middleware that runs before updating a resource

preUpdate: function (req, res, next) {
  performAsyncLogic(function (err) {
    next(err)
  })
}

new in 2.2

When findOneAndUpdate is disabled, the document is made available which is useful for authorization as well as setting values

findOneAndUpdate: false,
preUpdate: function (req, res, next) {
  if (req.erm.document.user !== req.user._id) {
    return res.sendStatus(401)
  }

  req.erm.document.set('lastRequestAt', new Date())

  next()
}

preDelete

function (req, res, next)2.1

Middleware that runs before deleting a resource

preDelete: function (req, res, next) {
  performAsyncLogic(function (err) {
    next(err)
  })
}

new in 2.2

When findOneAndRemove is disabled, the document is made available which is useful for authorization as well as performing non-destructive removals

findOneAndRemove: false,
preDelete: function (req, res, next) {
  if (req.erm.document.user !== req.user._id) {
    return res.sendStatus(401)
  }

  req.erm.document.deletedAt = new Date()
  req.erm.document.save().then(function (doc) {
    res.sendStatus(204)
  }, function (err) {
    options.onError(err, req, res, next)
  })
}

access

function (req[, done])

Returns or yields ‘private’, ‘protected’ or ‘public’. It is called on GET, POST and PUT requests and filters out the fields defined in private and protected

Examples

Sync

access: function (req) {
  if (req.isAuthenticated()) {
    return req.user.isAdmin ? 'private' : 'protected'
  } else {
    return 'public'
  }
}

Async

access: function (req, done) {
  performAsyncLogic(function (err, result) {
    done(err, result ? 'public' : 'private')
  })
}

contextFilter

function (model, req, done)

Allows request specific filtering

Example
contextFilter: function (model, req, done) {
  done(model.find({
    user: req.user._id
  }))
}

postCreate

function (req, res, next)2.0

Middleware that runs after successfully creating a resource

postCreate: function (req, res, next) {
  var result = req.erm.result         // object
  var statusCode = req.erm.statusCode // 201

  performAsyncLogic(function (err) {
    next(err)
  })
}

postRead

function (req, res, next)2.0

Middleware that runs after successfully reading a resource

postRead: function (req, res, next) {
  var result = req.erm.result         // object / array
  var statusCode = req.erm.statusCode // 200

  performAsyncLogic(function (err) {
    next(err)
  })
}

postUpdate

function (req, res, next)2.0

Middleware that runs after successfully updating a resource

postUpdate: function (req, res, next) {
  var result = req.erm.result         // object
  var statusCode = req.erm.statusCode // 200

  performAsyncLogic(function (err) {
    next(err)
  })
}

postDelete

function (req, res, next)2.0

Middleware that runs after successfully deleting a resource

postDelete: function (req, res, next) {
  var result = req.erm.result         // undefined
  var statusCode = req.erm.statusCode // 204

  performAsyncLogic(function (err) {
    next(err)
  })
}

outputFn

function (req, res)

Function used to output the result

Example
outputFn: function (req, res) {
  res.status(req.erm.statusCode).json(req.erm.result)
}

postProcess

function (req, res, next)2.0

Middleware that is called after output, useful for logging

Not guaranteed to execute after output if async operations are performed inside `outputFn`
postProcess: function (req, res, next) {
  console.log(`${req.method} ${req.path} request completed with response code ${req.erm.statusCode}`)
}

onError

function (err, req, res, next)send the entire mongoose error

Leaving this as default may leak information about your database

Function used to output an error

Example
onError: function (err, req, res, next) {
  next(err)
}

defaults

restify.defaults(options)

options: same as above, sets this object as the defaults for anything served afterwards