On 10/15/2012 01:18 AM, Mil Werns wrote:
@Tim:
Thanks, positive feedback is always welcome :)

@Eldar:
the-box looks very interesting. I am curious to see what in this project happens next. But currently, it is important for me to use only standard (within the meaning of established/mainstream) componentes and libraries (like e.g. async) that every node programmer knows.

Can you please point out some more details why you don't find this code clean/easy maintainable? And give me some example code for improvements?

It seems you are targeting db activities - since db's are so specific, I'm not sure it makes sense to try for a generic framework but might be more useful to have implementations for specific dbs. Couchdb is already REST - with node it makes sense to have a change listener and maybe an auth system. Mongodb is popular with Noders but lacks a js REST interface (the one available is not very useful) so anything targeted here would be quite useful. Might want to examine what python based sleepy mongoose does along these lines. might also be nice to do a REST interface for Postgres on the sql side.





On Sunday, October 14, 2012 11:58:31 AM UTC+2, Mil Werns wrote:

    Hi,

    I want to implement a REST API with node.js and restify. Because
    it is my first node.js project, I spent some time to learn how to
    structure the application to avoid the callback hell. Please have
    a look to the following solution.


    _*Assumptions*_
    - This approach is for writing an end-user application, not a
    module or a driver in the sense of a public library.
    - REST services are closely related to CRUD, so we have a lot of
    simple sequential/interdependent operations (get_data +
    validate_data [+ change_data] + save_data).


    _*Goals*_
    *===> Focus is writing maintainable, clean and easy to understand
    code! <===*
    Conversely, this means...
    - Performance is secondary (scaling horizontally if necessary).
    - Avoid "low level" methods like process.nextTick(), instead use
    "high level" methods/libs like middleware and control flow modules.
    - Use modules to implement a well known MVC structure.


    _*Code*_
    Please ignore the poor logging, the not very useful view tasks
    etc. in the following example. It should only demonstrate the MVC
    / callback structure.


    */* server.js */*

    var restify = require("restify");
    var router = require("./router");
    ...
    router.route(server);
    server.listen(...);


    */* router.js */*

    var userController = require("./controllers/user");
    ... // more controllers

    exports.route = function route(server) {
      server.get("/users",
        [
          userController.checkRead,
          userController.read,
        ]
      );
      server.put("/users",
        [
          userController.checkUpdate,
          userController.update,
        ]
      );
    ...
    };


    */* controllers/user.js */*

    var restify = require("restify");
    var async = require("async");
    var check = require('validator').check;
    var sanitize = require('validator').sanitize;
    var m = require("../models/user");
    var v = require("../views/user");
    ...

    var checkUpdate = function userCheckUpdate(req, res, next) {
      // validate user input
      try {
        check(req.params.id <http://req.params.id>, "id").isInt();
        check(req.params.forename, "forename").len(1,50);
        check(req.params.surname, "surname").len(1,50);
        check(req.params.email, "email").isEmail();
      } catch(err) {
        if(err) {
          return next(new restify.InvalidArgumentError("Invalid
    arguments"));
        }
      }
      return next();
    };


    var update = function userUpdate(req, res, next) {
      // create model
      var model = {
        forename: sanitize(req.params.forename).xss(),
        surname: sanitize(req.params.surname).xss(),
        email: sanitize(req.params.email).xss(),
      };

      //*** "sub-methods" part ***

      // _example_ for data check
      function checkOwner(curData, callback) {
        if(curData.ownerId !== req.userId) {
          return callback(new
    restify.InvalidArgumentError("NotAuthorizedError"));
        }
        return callback(null, curData);
      };

      // _example_ for simple model manipulation
      function addSimple(model, callback) {
        model.updateDate = new Date().getTime();
        return callback(null, model);
      };

      // _example_ for complex model manipulation
      function addComplex(model, callback) {
        // use async again to simulate synchronous/parallel/... flow
        async.waterfall(
          [
            // do something
          ]
          function(err, model) {
            if(err) {
              return callback(new restify.InternalError());
            }
    return callback(null, model);
          }
        );
      };


      // *** main part ***
      // use async to simulate synchronous flow
      async.waterfall(
        [
          function(aNext) { m.get(req.params.id
    <http://req.params.id>, aNext); },
          function(curData, aNext) { checkOwner(curData, aNext); },
          function(curData, aNext) { addSimple(model, aNext); },
          function(model, aNext) { addComplex(model, aNext); },
          function(model, aNext) { m.update(model, aNext); },
          function(model, aNext) { v.update(model, aNext); },
        ],
        function(err, model) {
          if(err) { return next(err); }
          res.send(200, model);
        }
      );
      return next();
    };
    ...


    */* models/user.js */*
    ...
    var get = function userGet(id, callback) {
      var curData = ... // DB query for ID
      if(err) {
        console.log("DB error: " + err);
        return callback(new restify.InternalError());
      };
      return callback(null, curData);
    };

    var update = ...
    ...


    */* views/user.js */*
    ...
    var update = function userUpdate(model, callback) {
      // adjust model for output
      delete model.updateDate;
      ...
      callback(null, model);
    };


    As you can see, this approach uses the middleware concept + async
    module to separate the application in small (MVC) pieces that are
    hopefully easy to understand.
    But as I said before: I'm a node.js newbie :)

    *What do you think about this structure?
    Are there any drawbacks?*

    Thanks a lot for your help
    Mil


--
Job Board: http://jobs.nodejs.org/
Posting guidelines: https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en

--
Job Board: http://jobs.nodejs.org/
Posting guidelines: 
https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en

Reply via email to