Restify-Mongoose provides a resource abstraction for restify to expose mongoose models as REST resources.
First you'll need to install restify-mongoose via npm
npm install restify-mongoose
Second step is to wire up mongoose and restify using restify-mongoose
var restify = require('restify');
var restifyMongoose = require('restify-mongoose');
var mongoose = require('mongoose');
var server = restify.createServer({
name: 'restify.mongoose.examples.notes',
version: '1.0.0'
});
server.use(restify.acceptParser(server.acceptable));
server.use(restify.queryParser());
server.use(restify.bodyParser());
// Create a simple mongoose model 'Note'
var NoteSchema = new mongoose.Schema({
title : { type : String, required : true },
date : { type : Date, required : true },
tags : [String],
content : { type: String }
});
var Note = mongoose.model('notes', NoteSchema);
// Now create a restify-mongoose resource from 'Note' mongoose model
var notes = restifyMongoose(Note);
// Serve resource notes with fine grained mapping control
server.get('/notes', notes.query());
server.get('/notes/:id', notes.detail());
server.post('/notes', notes.insert());
server.patch('/notes/:id', notes.update());
server.del('/notes/:id', notes.remove());
server.listen(3000, function () {
console.log('%s listening at %s', server.name, server.url);
});
To map resources or resource functionality to restify REST endpoints/routes restify-mongoose offers two approaches:
- 'fine grained mapping' control via list, detail, new, update and delete functions
- 'quick mapping' via serve method
Fine grained mapping
In the above getting started example we used fine grained mapping control. Restify-mongoose defines the functions query
,
detail
, insert
, update
and remove
that return restify route handlers and can be used like this:
// Serve resource notes with fine grained mapping control
server.get('/notes', notes.query());
server.get('/notes/:id', notes.detail());
server.post('/notes', notes.insert());
server.patch('/notes/:id', notes.update());
server.del('/notes/:id', notes.remove());
For every ´id´ dependent function the restify route has to define a :id
placeholder to allow restify-mongoose to access
id parameters. Id dependent functions are detail
, update
and delete
.
Query String
Setting a queryString
will make restify-mongoose use the string as the field name to conduct its searches in the detail
update
& remove
functions.
If not set it will use the default behavior of using mongos _id
field.
// Now create a restify-mongoose resource from 'Note' mongoose model and set queryString to 'myField'
var notes = restifyMongoose(Note, {queryString: 'myField'});
// these functions will now conduct searches with the field 'myField'. (defaults to '_id')
server.get('/notes/:id', notes.detail());
server.patch('/notes/:id', notes.update());
server.del('/notes/:id', notes.remove());
Quick mapping
// Serve resource notes with quick mapping
restifyMongoose(models.Note).serve('/api/notes', server);
Maps urls
- GET '/api/notes' to
query
function - GET '/api/notes/:id' to
detail
function - POST '/api/notes' to
insert
function - DELETE '/api/notes/:id' to
remove
function - PATCH '/api/notes/:id' to
update
function
You can also pass an options object to the serve
method to attach handlers before and after the request.
For example, to use restify-jwt:
// Serve resource notes with quick mapping with JWT auth
restifyMongoose(models.Note).serve('/api/notes', server, { before: jwt({secret: 'some-secret'}) } );
Query parameters are passed by query string parameter q.
Query parameters are parsed as JSON objects and passed to mongoose where query function.
To filter a notes resource by title to match term "first" append the q query parameter to the URL:
https://localhost:3000/notes?q={"title":"first"}
Requests that return multiple items in query
will be paginated to 100 items by default. You can set the pageSize
(number min=1, max=100) by adding it to the options.
var options = {
pageSize: 2
};
var notes = restifyMongoose(Note, options);
or as query string parameter pageSize
(which will have the presedence)
https://localhost:3000/notes?pageSize=2
You can specify further pages with the p parameter and a page number.
https://localhost:3000/notes?p=1
The pagination info is included in the Link header. It is important to follow these Link header values instead of constructing your own URLs.
link:
<https://example.com/notes?p=0>; rel="first",
<https://example.com/notes?p=1>; rel="prev",
<https://example.com/notes/?p=3>; rel="next",
<https://example.com/notes/?p=4>; rel="last"
Linebreak is included for readability.
You can set the baseUrl
by adding it to the options.
var options = {
baseUrl: 'https://example.com'
};
The possible rel
values are:
- next - Shows the URL of the immediate next page of results.
- last - Shows the URL of the last page of results.
- first - Shows the URL of the first page of results.
- prev - Shows the URL of the immediate previous page of results.
The total number of results/resources returned in query
is sent in the X-Total-Count Header
and is not affected by
pagination (setting pageSize
and p parameter). It does take in account filter and query parameter ( q ).
Sort parameters are passed by query string parameter sort.
Sort parameters can be separated by comma or space. They will be passed directly to mongoose sort query function.
To sort a notes resource by title descending append the sort query parameter to the URL:
https://localhost:3000/notes?sort=-title
You can also define a default sort in the options object. This option will by ignored if a sort query parameter exists.
Using in the constructor:
var notes = restifyMongoose(Note, {sort: '-title'});
notes.serve('/notes', restifyServer);
Using for query or detail methods:
var notes = restifyMongoose(Note);
note.query({sort: '-title'});
## Select Fields
To restrict selected columns you can pass a query string parameter __select__.
Select fields can be separated by comma or space. They will be passed to [mongoose select query function](http://mongoosejs.com/docs/api.html#query_Query-select).
To select only title and date the fields of a notes resource append the __select__ query parameter to the URL:
http://localhost:3000/notes?select=title,date
You can also define select fields in the options object. This will make the the __select__ query parameter be ignored.
Using in the constructor:
```javascript
var notes = restifyMongoose(Note, {select: 'title'});
notes.serve('/notes', restifyServer);
Using for query or detail methods:
var notes = restifyMongoose(Note);
note.detail({select: 'title,date,tags'});
note.query({select: 'title date'});
Results can be filtered with a function, which is set in the options object of the constructor or on the query
and detail
function.
The function takes two parameters: the request object and the response object. The return value of the function is a query that is passed directly to the mongoose where query function.
For instance, you can use a filter to display only results for a particular user:
var filterUser = function(req, res) {
return {user: req.user};
}
var notes = restifyMongoose(Note, {filter: filterUser});
A projection is a function, used by the query
and detail
operations, which takes the request object, the result model, and a callback. This function should invoke the callback exactly once. This callback takes an error and a model item as it's two parameters. Use null
for the error is there is no error.
For instance, the default detail and list projections are as follows:
function (req, item, cb) {
cb(null, item);
};
A projection is useful if you need to manipulate the result item before returning it in the response. For instance, you may not want to return the passwordHash for a User data model.
// If this is the schema
var UserSchema = new Schema({
username: String,
email: String,
passwordHash: String
});
// This is a projection translating _id to id and not including passwordHash
var userProjection = function(req, item, cb) {
var user = {
id: item._id,
username: item.username,
email: item.email
};
cb(null, user);
};
Projection functions are specified in the options for the resitfy-mongoose contructor, the query function, or the detail function.
For the construtor, the options are listProjection
and detailProjection
var users = restifyMongoose(User, {listProjection: userProjection, detailProjection: userProjection});
users.serve('/users', restifyServer);
For both query and detail, the option is projection
var users = restifyMongoose(User);
users.detail({projection: userProjection});
users.query({projection: userProjection});
The output format can be changed to a more compatible one with the json-api standard to use the API with frameworks like Ember.
var users = restifyMongoose(User, {outputFormat: 'json-api'});
users.serve('/users', restifyServer);
``
Also you can specify a custom model name like this:
```javascript
var users = restifyMongoose(User, {outputFormat: 'json-api', modelName: 'admins'});
users.serve('/users', restifyServer);
The returned results can use mongoose's "populate" query modifier to populated referenced documents within models.
Referenced documents can be populated in three ways:
Adding populate=[referenced_field]
to the query string will populate the referenced_field
, if it exists.
// e.g.
var notes = restifyMongoose(Note, {populate: 'author'});
// e.g.
server.get('/notes', notes.query({populate: 'author'}))
server.get('/notes/:id', notes.detail({populate: 'author'}))
Multiple referenced documents can be populated by using a comma-delimited list of the desired fields in any of the three methods above.
// e.g.
var notes = restifyMongoose(Note, {populate: 'author,contributors'});
Contribution welcome! Read the contribution guideline first.