Hi, this looks nice. I will use the middleware approach in your routers ;-)
Since i am learning myself you might want to question my "tip". Instead of a structure like: - controller -- userController -- nextController -- and so on - views -- userView -- nextView -- and so on - models -- userModel -- nextModel -- and so on - onlyOneRouter.js I personally find it very helpful to structure the mvc separated in modules. Also the router like: - modules/ -- userModule/ --- UserController.js --- Router.js --- models/ ---- UserModel.js --- views/ ---- UserView.js -- nextModule/ --- NextController.js --- Router.js --- models/ ---- NextModel.js --- views/ ---- NextView.js This way you separate your routers too. I personally found https://github.com/visionmedia/express/tree/master/examples/mvc very helpful for structuring. I also like how the app is separated in "sub-apps" via the lib/boot.js in the mvc example from express. If i got something wrong, please feel free to correct me ;-) Am Sonntag, 14. Oktober 2012 11:58:31 UTC+2 schrieb Mil Werns: > > 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, "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, 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
