Serves Mongoose models on an extensible RESTful API.
Features at a Glance
- Tight integration with Mongoose and Express.
- An Express middleware. Put it on its own route and the rest of your code is left untouched.
- Configured within the Mongoose schema. No need to deal with messy configuration objects.
- Everything you'd ever need from a REST API (other than auth) is already included.
- Middleware supported on each route, so integration with things like Passport is very simple
- Flexible query filtering system.
- Document transformer system. Control what gets sent to which clients.
- Built with Angular in mind.
- Test coverage. While probably not at 100%, it is pretty well covered.
This module is installed via npm:
$ npm install restifier --save
The following example serves the
Badge models on a RESTful API.
var express = ;var restifier = ;var app = ;app; // Requiredvar User =;// A nested routevar Badge = User;app; // Serve the api on /api.app;
Restifier uses the MongoDB collection name to determine the name of the base route, so the
User model would create routes under
Querying takes in the following parameters:
fieldwith any field in your Mongoose model, and it will check for equality.
populate- Comma-delimited list of fields to populate
sort- Sorts by the given fields in the given order, comma delimited. A
-sign will sort descending.
limit- Limits the number of returned results.
skip- Skips a number of results. Useful for pagination when combined with
filter- Applies a filter. See the Filters section for more details.
GET /users GET /users?field=value GET /users?populate=posts,comments GET /users?sort=field,-field2 GET /users?limit=10&skip=10 GET /users?filter=filter1|filter2 GET /users/Bob/badges?sort=date
POST /users POST /users/Bob/badges
Get supports one parameter, the
GET /users/Bob GET /users/Bob?populate=posts GET /users/Bob/badges/1 GET /users/Bob/badges/1?populate=things
PUT and PATCH are handled the same way.
PUT /users/Bob PATCH /users/Bob PUT /users/Bob/badges/1 PATCH /users/Bob/badges/1
DELETE /users/Bob DELETE /users/Bob/badges/1
Creating an API
First, declare all of your models using
restifier(mongooseModel). This function returns a
Model object which can be altered. (see the JSDocs)
Next, serve the API as middleware:
This will create a middleware that will be used by Express.
In the case of namespace collision, routes are handled sequentially by Express. Declare your custom routes before using the middleware. For example:
app.post('/api/login', myLoginHandler); app.use('/api', restifier.middleware());
is the appropriate way to add functionality to your API.
There are 5 types of routes: query, create, get, update, and destroy. You can apply middleware to a single one of these routes by doing the following:
You can also apply middleware to all of a model's routes:
The following fields are exposed in the request object:
doc-- The document being retrieved, or null if not operating on a document route
parentDoc-- The parent document being retrieved. Used for nested routes.
sortfields are parsed beforehand,
populatebeing an Array of Strings and
sortbeing an object.
Authentication middleware example with Passport
Here is an example of using Passport to restrict access to a document:
Passport exposes a
user property on the request, so we can deal with that directly in our middleware. If we were to use something like connect-roles, we would do something like this:
The Query Pipeline
Restifier was designed to be very flexible so it could be used as a backend for any app. Thus, queries go through a series of steps before being transformed into what is sent to the client.
Modifiers --> Parameters --> Filters --> Population --> Execution --> Transformers
Modifiers alter the query parameters that will be passed to the pipeline. For example, you could have a modifier that forces sorting by name ascending, as shown below:
To modify a parameter, just pass the name of the parameter you wish to modify and a callback that returns the modified value of the parameter.
populate are the only parameters that are objects.
sort parameter looks like this:
name: 1 // Ascendingdate: -1 // Descending
populate parameter looks like this:
'users' 'comments' 'posts'
There are 4 types of parameters: limit, skip, sort, and field equality. These are all described in the Query section.
Filters are user-defined functions that modify the query. They work very similarly to AngularJS filters. They can be chained and take parameters, allowing immense flexibility for developers to add features to APIs.
Filters are defined as follows:
Here is an example of a filter that takes parameters:
This filter would be called using
proximity 5 if one wanted to check if the location was within a distance of 5.
Chaining filters is pretty simple; just use the
| (pipe) operator to do so.
GET /people?filter=children | proximity 5
Fields that were marked for population in the query are now populated. You can change what fields are returned using population transformers.
At this point in the pipeline,
query.exec() is called and we query the database.
Transformers change the returned results. One transformer is built in, the
restricted transformer, and cannot be changed. Here is an example of using a transformer:
Transformers are applied to each individual document in a query result.
Population transformers are transformers that operate on populated fields. They can be used to make your application more secure by removing fields you don't want people to see.
Here are some apps that use Restifier. If you have one you'd like to share, please don't be afraid to send a PR!
- todo-restifier - A Restifier-powered Todo app made with Angular, Restangular, Bootstrap, and Restifier.
Copyright (c) 2014 Ian Macalinao. Released under the MIT License, which can be viewed in the attached