package.json
{
"dependencies": {
"express-restify-mongoose": "^3.0.0",
"mongoose": "^4.0.0"
}
}
From the command line
npm install express-restify-mongoose --save
While the source and examples are now written in ES2015, the module is transpiled and published as ES5 using Babel and remains fully compatible with Node 0.10 and newer.
This snippet…
const express = require('express')
const bodyParser = require('body-parser')
const methodOverride = require('method-override')
const mongoose = require('mongoose')
const restify = require('express-restify-mongoose')
const app = express()
const 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/Customer/count
GET http://localhost/api/v1/Customer
POST http://localhost/api/v1/Customer
DELETE http://localhost/api/v1/Customer
GET http://localhost/api/v1/Customer/:id
GET http://localhost/api/v1/Customer/:id/shallow
PUT http://localhost/api/v1/Customer/:id
POST http://localhost/api/v1/Customer/:id
PATCH http://localhost/api/v1/Customer/:id
DELETE http://localhost/api/v1/Customer/:id
let request = require('request')
request({
url: '/api/v1/Model',
qs: {
query: JSON.stringify({
$or: [{
name: '~Another'
}, {
$and: [{
name: '~Product'
}, {
price: '<=10'
}]
}],
price: 20
})
}
})
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
GET /Customer?sort=name
GET /Customer?sort=-name
GET /Customer?sort={"name":1}
GET /Customer?sort={"name":0}
GET /Customer?skip=10
Only overrides options.limit
if the queried limit is lower
GET /Customer?limit=10
Supports all operators ($regex, $gt, $gte, $lt, $lte, $ne, etc.) as well as shorthands: ~, >, >=, <, <=, !=
GET /Customer?query={"name":"Bob"}
GET /Customer?query={"name":{"$regex":"^(Bob)"}}
GET /Customer?query={"name":"~^(Bob)"}
GET /Customer?query={"age":{"$gt":12}}
GET /Customer?query={"age":">12"}
GET /Customer?query={"age":{"$gte":12}}
GET /Customer?query={"age":">=12"}
GET /Customer?query={"age":{"$lt":12}}
GET /Customer?query={"age":"<12"}
GET /Customer?query={"age":{"$lte":12}}
GET /Customer?query={"age":"<=12"}
GET /Customer?query={"age":{"$ne":12}}
GET /Customer?query={"age":"!=12"}
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"}]
_id
is always returned unless explicitely excluded
GET /Customer?select=name
GET /Customer?select=-name
GET /Customer?select={"name":1}
GET /Customer?select={"name":0}
If the field is private or protected and the request does not have appropriate access, an empty array is returned
GET /Customer?distinct=name
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
When version is unspecified, the feature is available in the initial major release (3.0.0)
string/api
Path to prefix to the REST endpoint.
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.
Generates /api/v1/Entities/:id/Model
and /api/v1/Entities/Model
for all pertinent methods.
version: '/v1/Entities/:id'
string_id
findById
will query on the given property.
booleanfalse
Enable support for restify instead of express.
stringmodel name
Endpoint name
booleantrue3.2
Whether or not regular expressions should be executed. Setting it to true
will protect against ReDoS, see issue #195 for details.
booleanfalse
Whether or not mongoose should run schema validators when using findOneAndUpdate
. For more information, read the mongoose docs.
stringprimary
Determines the MongoDB nodes from which to read. For more information, read the mongoose docs.
boolean|stringfalse
When totalCountHeader: true
, execute a count query on GET /Model
requests ignoring limit and skip and setting the result in the a response header. It can also be set to a string to allow for a custom header. This is useful when it’s necessary to know in advance how many matching documents exist.
Boolean
totalCountHeader: true
Response:
Headers: {
'X-Total-Count': 5
}
String
totalCountHeader: 'X-Custom-Count-Header'
Response:
Headers: {
'X-Custom-Count-Header': 5
}
array
Array of fields which are only to be returned by queries that have private
access.
Defined in options
private: ['topSecret', 'fields']
Defined in mongoose schema
new Schema({
topSecret: { type: String, access: 'protected' },
fields: { type: String, access: 'protected' }
})
array
Array of fields which are only to be returned by queries that have private
or protected
access.
Defined in options
protected: ['somewhatSecret', 'keys']
Defined in mongoose schema
new Schema({
somewhatSecret: { type: String, access: 'protected' },
keys: { type: String, access: 'protected' }
})
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.
booleantrue
Whether to use .findOneAndUpdate()
or .findById()
and then .save()
, allowing document middleware to be called. For more information regarding mongoose middleware, read the docs.
booleantrue
Whether to use .findOneAndRemove()
or .findById()
and then .remove()
, allowing document middleware to be called. For more information regarding mongoose middleware, read the docs.
function (req, res, next)
Middleware that runs before preCreate, preRead, preUpdate and preDelete.
preMiddleware: function (req, res, next) {
performAsyncLogic((err) => {
next(err)
})
}
function (req, res, next)
Middleware that runs before creating a resource.
preCreate: function (req, res, next) {
performAsyncLogic((err) => {
next(err)
})
}
function (req, res, next)
Middleware that runs before reading a resource.
preRead: function (req, res, next) {
performAsyncLogic((err) => {
next(err)
})
}
function (req, res, next)
Middleware that runs before updating a resource.
preUpdate: function (req, res, next) {
performAsyncLogic((err) => {
next(err)
})
}
When findOneAndUpdate: false
, the document is 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()
}
function (req, res, next)
Middleware that runs before deleting a resource.
preDelete: function (req, res, next) {
performAsyncLogic((err) => {
next(err)
})
}
When findOneAndRemove: false
, the document is 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)
})
}
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.
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')
})
}
function (model, req, done)
Allows request specific filtering.
contextFilter: function (model, req, done) {
done(model.find({
user: req.user._id
}))
}
function (req, res, next)
Middleware that runs after successfully creating a resource. The unfiltered document is available on req.erm.result
.
postCreate: function (req, res, next) {
let result = req.erm.result // unfiltered document or object
let statusCode = req.erm.statusCode // 201
performAsyncLogic((err) => {
next(err)
})
}
function (req, res, next)
Middleware that runs after successfully reading a resource. The unfiltered document(s), or object(s) when lean: false
, is available on req.erm.result
.
postRead: function (req, res, next) {
let result = req.erm.result // unfiltered document, object or array
let statusCode = req.erm.statusCode // 200
performAsyncLogic((err) => {
next(err)
})
}
function (req, res, next)
Middleware that runs after successfully updating a resource. The unfiltered document, or object when lean: false
, is available on req.erm.result
.
postUpdate: function (req, res, next) {
let result = req.erm.result // unfiltered document or object
let statusCode = req.erm.statusCode // 200
performAsyncLogic((err) => {
next(err)
})
}
function (req, res, next)
Middleware that runs after successfully deleting a resource.
postDelete: function (req, res, next) {
let result = req.erm.result // undefined
let statusCode = req.erm.statusCode // 204
performAsyncLogic((err) => {
next(err)
})
}
function (req, res)
Function used to output the result. The filtered object is available on req.erm.result
.
outputFn: function (req, res) {
let result = req.erm.result // filtered object
res.status(req.erm.statusCode).json(result)
}
function (req, res, next)
Middleware that is called after output, useful for logging. The filtered object is available on req.erm.result
.
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}`)
}
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.
onError: function (err, req, res, next) {
next(err)
}
restify.defaults(options)
options: same as above, sets this object as the defaults for anything served afterwards