This is a sketch of what we want:
var Proto = require('uberproto'),
resource = require('express-resource');
// in controllers/organisations.js
module.exports = function(app) {
var ctrl = OrganisationsController.create();
app.resource('/url', ctrl);
app.get('/api/v1/orginations/:id/venue', ctrl.relationRoute('venue'));
}
var OrganisationsController = BaseController.extend({
/**
Limit the returned organisations to those that the user can see
*/
getConditions: function(req) {
return {
user_id: req.user.id
}
}
});
// in basement/lib/BaseController
var BaseController = Proto.extend({
// see api.js below
});
This is the code from my last app:
var nullMiddleware = function(req, res, next) { next(); };
exports.defineReadRoutes = function(app, Model, options) {
options = options || {};
// getConditions is a middleware function that may provide conditions for the query
var getConditions;
// if `options` includes a function that provides conditions for the query
if (options.getConditions) {
getConditions = function(req, res, next) {
// call the provided function
options.getConditions(req, options)
.then(function(conditions) {
// place the conditions into the req object
req.queryConditions = conditions;
// and continue down the middleware stack
next();
}, function(err) {
// the provided function can throw the special value `api.NO_RESULTS`
// to indicate that no results should be returned
if (err == api.NO_RESULTS) {
req.queryConditions = api.NO_RESULTS;
// and this does not indicate an error
return next();
}
// otherwise pass the error down the middleware stack
next(err);
});
};
}
else { // just insert a "do nothing" middleware function in the stack
getConditions = nullMiddleware;
}
Model = getModel(Model);
app.get('/api/v1/' + Model.routeName, getConditions, function(req, res) {
// create a PageableQuery object
var query = new PageableQuery(req, options, req.queryConditions);
Q()
.then(function() {
// if no results should be returned then return an empty result
if (req.queryConditions === api.NO_RESULTS) return new PageableQueryResult();
// otherwise perform the query
return api.paginate(Model, query.conditions, query.page, query.pageSize, query.options);
})
.then(function(page) {
// then display the results
var results = new Array(page.results.length);
for (var i = 0; i < page.results.length; i++) {
results[i] = page.results[i].serialize();
}
var response = {
meta: {
status: 200,
page: query.page,
total: page.count,
pageSize: query.pageSize
}
};
response[Model.routeName] = results;
res.json(response);
}, exports.errorHandlerFn(req, res));
});
app.get('/api/v1/' + Model.routeName + '/:id', getConditions, exports.modelFinder(Model), read);
app.get('/api/v1/' + Model.routeName + '/:id/read', getConditions, exports.modelFinder(Model), read);
function read(req, res) {
var response = {};
response[Model.namespace] = req.model.serialize();
res.json(response);
}
};
exports.defineWriteRoutes = function(app, Model, options) {
options || (options = {});
options.callbacks || (options.callbacks = {});
Model = getModel(Model);
app.post('/api/v1/' + Model.routeName, create);
function create(req, res) {
var props = Model.deserialize(req.body[Model.namespace]);
Model.create(props, function(err, instance) {
if (err) return exports.handleError(err, req, res);
if (options.callbacks.create) {
options.callbacks.create(instance, req, res);
} else {
res.json(saveSuccess(instance, 'Created'));
}
});
}
var modelFinder = exports.modelFinder(Model);
app.put('/api/v1/' + Model.routeName + '/:id', modelFinder, save);
app.post('/api/v1/' + Model.routeName + '/:id', modelFinder, save);
function save(req, res) {
var model = req.model,
props = Model.deserialize(req.body[Model.namespace]);
Object.keys(props).forEach(function(prop) {
model[prop] = props[prop];
});
model.save(function(err, saved) {
if (err) return exports.handleError(err, req, res);
if (options.callbacks.update) {
options.callbacks.update(saved, req, res);
} else {
res.json(saveSuccess(saved, 'Saved'));
}
});
}
app.delete('/api/v1/' + Model.routeName + '/:id', modelFinder, doDelete);
function doDelete(req, res) {
req.model.remove(function(err) {
if (err) return api.handleError(err, req, res);
if (options && options.callbacks && options.callbacks.delete) {
options.callbacks.delete(req, res);
} else {
res.json({});
}
});
}
};
var saveSuccess = function (model, message) {
var response = {
meta: {
status: 200,
message: message,
}
};
response[model.constructor.namespace] = model.serialize();
return response;
};
exports.saveSuccess = saveSuccess;
exports.defineRelationRoute = function(app, Parent, Child, method, options) {
Parent = getModel(Parent);
Child = getModel(Child);
var route = '/api/v1/' + Parent.routeName + '/:id/' + Child.routeName;
app.get(route, api.modelFinder(Parent), function(req, res) {
var parent = req.model;
parent[method].call(parent, function(err, children) {
if (err) return api.handleError(err, req, res);
var response = {};
response[Child.routeName] = children.map(function(a) {
return a.serialize();
});
res.json(response);
});
});
};
function getModel(Model) {
if (typeof Model === 'string') {
require('../models/' + dasherize(Model));
Model = mongoose.model(Model);
}
return Model;
}