bonez
bonez

Reputation: 695

How to scope data in express / node controller

I'm trying to avoid callback hell by breaking down my Express / Kraken.js controller into smaller callback functions.

I was processing a request and had about 6 levels of nested anonymous callback functions.

so now I have my main function that looks like this:

// ugh, I know this isn't right
var globalProducts = {};

module.exports = function (server) {

    server.get('/project', function (req, res) {
        var data = req.query;
        globalData = data; 

        if(!data.projectId || !data.ruleSetId) 
            res.json({error: "Incomplete input data."});

             // pass response to products call back 
        Project.findOne({ _id: mongoose.Types.ObjectId(data.projectId) }, setUpProducts);   
    });
};

function setUpProducts(err, project){
    // get all products and pass them down the pipe
    project.findAllChildren(setUpRules);
}

function setUpRules(err, products) {
    // we need to access products in another function 
    globalProducts = products; 
    // find the rule set and build the rule Flow
    RuleSet.findOne({ _id: mongoose.Types.ObjectId(globalData.ruleSetId) }, function(err, ruleSet) { 
        ruleSet.buildFlow(processFlow);
    });
}

My question is what is the best way to pass around info between callbacks ? My solution was var globalProducts = {}; but to me, the controller contain any 'global state' .. whats the best way to handle this ?

Upvotes: 0

Views: 788

Answers (1)

vkurchatkin
vkurchatkin

Reputation: 13570

Doing this is a bad idea. It will cause race condition type issue — basically it's the same as sharing data in multithreaded environment. Instead you can use req or res to store data. To do that you need them in scope, so you can define all functions inside route handler or make each function a middleware so it will have req and res as arguments. Here is an example of this approach:

function check (req, res, next) {
  if(!req.query.projectId || !req.query.ruleSetId) return res.json({error: "Incomplete input data."});  
  next()
}

function findProject (req, res, next) {
  Project.findOne({ _id: mongoose.Types.ObjectId(req.query.projectId) }, after);  

  function after (err, project) {
    if (err) return next(err);
    req.project = project;
    next();
  }
}

function findProducts (req, res, next) {
   req.project.findAllChildren(after)

  function after (err, products) {
    if (err) return next(err);
    req.products = products;
    next();
  }
}

function respond (req, res) {
  res.render('view', {
    products : req.products,
    project : req.project
  });
}


module.exports = function (server) {
   server.get('/project', check, findProject, findProducts, respond);
};

Upvotes: 3

Related Questions