Skip to content

Instantly share code, notes, and snippets.

@simonexmachina
Created May 19, 2014 02:06
Show Gist options
  • Save simonexmachina/626a3b98230697d351c7 to your computer and use it in GitHub Desktop.
Save simonexmachina/626a3b98230697d351c7 to your computer and use it in GitHub Desktop.
BaseController

OrganisationsController

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
});

api.js

This is the code from my last app:

defineReadRoutes

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);
  }
};

defineWriteRoutes

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;

defineRelationRoute

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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment