package.json
{
"dependencies": {
"express-restify-mongoose": "^5.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:27017/database')
restify.serve(router, mongoose.model('Customer', new mongoose.Schema({
name: { type: String, required: true },
comment: { type: String }
})))
app.use(router)
app.listen(3000, () => {
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
const request = require('request')
request.get({
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.
Use 1
for ascending and -1
for descending order.
GET /Customer?sort=name
GET /Customer?sort=-name
GET /Customer?sort={"name":1}
GET /Customer?sort={"name":-1}
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.)
GET /Customer?query={"name":"Bob"}
GET /Customer?query={"name":{"$regex":"^(Bob)"}}
GET /Customer?query={"age":{"$gt":12}}
GET /Customer?query={"age":{"$gte":12}}
GET /Customer?query={"age":{"$lt":12}}
GET /Customer?query={"age":{"$lte":12}}
GET /Customer?query={"age":{"$ne":12}}
Population is the process of automatically replacing the specified paths in the document with document(s) from other collection(s).
restify.serve(router, mongoose.model('Invoice', new mongoose.Schema({
customer: [{ type: mongoose.Schema.Types.ObjectId }],
products: [{ type: mongoose.Schema.Types.ObjectId }]
})))
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"}]
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
const uri = restify.serve(router, model[, options])
// uri = '/api/v1/Model'
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 (4.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
booleantrue
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.
(req, res, next) => {}
Middleware that runs before preCreate, preRead, preUpdate and preDelete.
preMiddleware: (req, res, next) => {
performAsyncLogic((err) => {
next(err)
})
}
(req, res, next) => {}
Middleware that runs before creating a resource.
preCreate: (req, res, next) => {
performAsyncLogic((err) => {
next(err)
})
}
(req, res, next) => {}
Middleware that runs before reading a resource.
preRead: (req, res, next) => {
performAsyncLogic((err) => {
next(err)
})
}
(req, res, next) => {}
Middleware that runs before updating a resource.
preUpdate: (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: (req, res, next) => {
if (req.erm.document.user !== req.user._id) {
return res.sendStatus(401)
}
req.erm.document.set('lastRequestAt', new Date())
next()
}
(req, res, next) => {}
Middleware that runs before deleting a resource.
preDelete: (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: (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((doc) => {
res.sendStatus(204)
}).catch((err) => {
options.onError(err, req, res, next)
})
}
(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: (req) => {
if (req.isAuthenticated()) {
return req.user.isAdmin ? 'private' : 'protected'
} else {
return 'public'
}
}
Async
access: (req, done) => {
performAsyncLogic((err, result) => {
done(err, result ? 'public' : 'private')
})
}
(model, req, done) => {}
Allows request specific filtering.
contextFilter: (model, req, done) => {
done(model.find({
user: req.user._id
}))
}
(req, res, next) => {}
Middleware that runs after successfully creating a resource. The unfiltered document is available on req.erm.result
.
postCreate: (req, res, next) => {
const result = req.erm.result // unfiltered document or object
const statusCode = req.erm.statusCode // 201
performAsyncLogic((err) => {
next(err)
})
}
(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: (req, res, next) => {
const result = req.erm.result // unfiltered document, object or array
const statusCode = req.erm.statusCode // 200
performAsyncLogic((err) => {
next(err)
})
}
(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: (req, res, next) => {
const result = req.erm.result // unfiltered document or object
const statusCode = req.erm.statusCode // 200
performAsyncLogic((err) => {
next(err)
})
}
(req, res, next) => {}
Middleware that runs after successfully deleting a resource.
postDelete: (req, res, next) => {
const result = req.erm.result // undefined
const statusCode = req.erm.statusCode // 204
performAsyncLogic((err) => {
next(err)
})
}
(req, res) => {}4.3 (Promise support)
Function used to output the result. The filtered object is available on req.erm.result
. Using the async version allows handling errors through onError.
Sync
outputFn: (req, res) => {
const result = req.erm.result // filtered object
const statusCode = req.erm.statusCode // 200 or 201
res.status(statusCode).json(result)
}
Async (using promises)
outputFn: (req, res) => {
return performAsyncLogic().then() => {
const result = req.erm.result // filtered object
const statusCode = req.erm.statusCode // 200 or 201
return res.status(statusCode).json(result)
})
}
Async (using async/await)
outputFn: async (req, res) => {
const asyncLogicResult = await performAsyncLogic()
const result = req.erm.result // filtered object
const statusCode = req.erm.statusCode // 200 or 201
return res.status(statusCode).json(result)
}
(req, res) => {}
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: (req, res) => {
const result = req.erm.result // filtered object
const statusCode = req.erm.statusCode // 200 or 201
console.info(`${req.method} ${req.path} request completed with status code ${statusCode}`)
}
(err, req, res, next) => {}serialize the entire error, except stack
Leaving this as default may leak information about your database
Function used to output an error.
onError: (err, req, res, next) => {
const statusCode = req.erm.statusCode // 400 or 404
res.status(statusCode).json({
message: err.message
})
}
restify.defaults(options)
options: same as above, sets this object as the defaults for anything served afterwards