Skip to content

Instantly share code, notes, and snippets.

@dhigginbotham
Last active August 29, 2015 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dhigginbotham/f9626da2a0ebcb24f305 to your computer and use it in GitHub Desktop.
Save dhigginbotham/f9626da2a0ebcb24f305 to your computer and use it in GitHub Desktop.
simple express rest app... yeah.
_ = require "lodash"
rest = (opts) ->
# simple prefix, ie http://localhost:1337/api/:collection/:id?
@prefix = '/api'
@path = null
@key = 'collection'
# pre doing db, so if you want to pass in some
# overrides, this is where you'd do it.
@preware = []
# afterware, ie getting the object
@afterware = []
# support method types
@supports = ['post', 'put', 'get', 'delete']
@model = null
if opts? then _.extend @, opts
# define our context
self = @
if not @path?
# define model path
@path = '/' + self.model.modelName.toLowerCase();
# check if `path` has an 's' at the end
if @path[@path.length - 1] != 's' then @path += 's'
# router function
@router = (req, res, next) ->
method = req.method.toLowerCase()
return if self.supports.indexOf(method) == -1 then next(JSON.stringify({error: 'This is an unsupported/restricted method.'}))
switch method
when 'get' then self.get req, (err, resp) ->
return if err? then next err, null
req[self.key] = resp
next()
when 'post' then self.post req, (err, resp) ->
return if err? then next err, null
req[self.key] = resp
next()
when 'put' then self.put req, (err, resp) ->
return if err? then next err, null
req[self.key] = resp
next()
when 'delete' then self.delete req, (err, resp) ->
return if err? then next err, null
req[self.key] = resp
next()
@endpoint = (req, res) ->
res.json req[self.key]
@
rest::get = (req, fn) ->
self = @
id = if req.params.id then {_id: req.params.id} else {}
self.model.find id, (err, docs) ->
return if err? then fn err, null
fn null, docs
rest::save = (req, fn) ->
self = @
newCollection = new self.model req.body
newCollection.save (err, docs) ->
return if err? then fn err, null
fn null, docs
rest::update = (req, fn) ->
self = @
id = {_id: req.params.id}
update = _.omit(req.body, '_id')
self.model.update id, update, {upsert: true}, (err, updated) ->
# check for existence of errors
return if err? then fn err, null
fn null, updated
rest::post = (req, fn) ->
self = @
if typeof req.params.id != 'undefined' then self.update req, (err, updated) ->
return if err? then fn err, null
fn null, updated
else self.save req, (err, saved) ->
return if err? then fn err, null
fn null, saved
rest::put = (req, fn) ->
self = @
if typeof req.params.id != 'undefined'
self.update req, (err, updated) ->
return if err? then fn err, null
fn null, updated
else fn null, {error: 'You must include an \'/:id\' param'}
rest::delete = (req, fn) ->
self = @
id = if req.params.id then {_id: req.params.id} else {}
self.model.remove id, (err, removed) ->
return if err? then fn err, null
fn null, removed
rest::mount = (app) ->
self = @
uri = self.prefix + self.path + '/:id?'
app.all uri, self.preware, self.router, self.afterware, self.endpoint
module.exports = rest
;(function() {
var _ = require('lodash');
function Rest(opts, app) {
//public options
this.prefix = '/api';
this.path = null;
this.key = 'collection';
this.preware = [];
this.afterware = [];
this.supports = ['post', 'put', 'get', 'delete'];
this.model = null;
// extend options
if (opts) _.extend(this, opts);
// maintain context
var self = this;
if (!this.path) {
this.path = '/' + this.model.modelName.toLowerCase();
if (this.path[this.path.length-1] != 's') this.path += 's';
}
this.router = function router(req, res, next) {
var method = req.method.toLowerCase();
if (self.supports.indexOf(method) == -1) {
return next(JSON.stringify({
error: 'This HTTP Method is unsuported'
}), null);
}
function defaultCallback(err, resp) {
if (err) return next(err, null);
req[self.key] = resp;
return next();
};
return self[method](req, defaultCallback);
};
this.endpoint = function endpoint(req, res) {
return res.json(req[self.key]);
};
return this.mount(app);
};
Rest.prototype.createDocument = function _createDocument(req, fn) {
var newCollection = new this.model(req.body);
newCollection.save(fn);
};
Rest.prototype.updateDocument = function _updateDocument(req, fn) {
var id = (req.params.id ? {_id: req.params.id} : null);
var update = _.omit(req.body, '_id');
this.model.update(id, update, {upsert: true}, fn);
};
Rest.prototype.mount = function _mount(app) {
var url = this.prefix + this.path + '/:id?';
app.all(url, this.preware, this.router, this.afterware, this.endpoint);
return this;
};
Rest.prototype.get = function _get(req, fn) {
var id = (req.params.id ? {_id: req.params.id} : {});
this.model.find(id, fn);
};
Rest.prototype.post = function _post(req, fn) {
if (req.params.id) {
this.updateDocument(req, fn);
} else {
this.createDocument(req, fn);
}
};
Rest.prototype.put = function _put(req, fn) {
if (req.params.id) {
this.updateDocument(req, fn);
} else {
return fn(null, {
error: 'A valid id is required for this type of request'
});
}
};
Rest.prototype.delete = function _delete(req, fn) {
var id = (req.params.id ? {_id: req.params.id} : null);
this.model.remove(id, fn);
};
if (typeof module != 'undefined' && module.exports) {
module.exports = Rest;
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment