I recommend to have a look at the-box <https://github.com/eldargab/the-box>
just
because it suggests build systems style computational model which is
definitely worth to consider. Also there is
an interesting "Out of the tar pit" article. It's not directly related to
node but it mentions an idea of separation between business logic and
control flow. The interesting point is that it seems that Node kinda
encourages you to do this separation. While an emulation of classic sync
control flows may be a bad idea.
On Sunday, October 14, 2012 1:58:31 PM UTC+4, 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, "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