Search code examples
node.jsexpressarrow-functions

How should I pass three arguments to an express/node arrow function?


I am declaring my function like this:

const parseConnections = (connectionsCSVPath, req, res) => { 
     //do a bunch of stuff 
} 

Inside the function, if I try to call res.locals.something, I get an error saying "cannot read property locals of undefined" I have tried several other syntaxes, such as this:

const parseConnections = ((connectionsCSVPath, req, res) => { 
     //do a bunch of stuff 
}) 

this:

const parseConnections = (connectionsCSVPath, (req, res) => { 
     //do a bunch of stuff 
}) 

and this:

const parseConnections = connectionsCSVPath, (req, res) => { 
     //do a bunch of stuff 
} 

and they all throw errors. What is the proper way to pass these 3 arguments to the function so that all 3 are defined inside?

Edit*: The function is then called like this:

router.post(
'/upload/engagements/batch', checkIfAuthenticated,
parseConnections('./tmp/connections.csv'), 
parseMessages('./tmp/messages.csv'), (req, res) => { 
    //do a bunch of stuff 
}

Solution

  • The problem is not (necessarily) with how you define the function but with how you are using it.

    parseConnections('./tmp/connections.csv') calls the function right then and there. You are only passing a single argument to it, so req and res will be undefined.

    function foo(a, b, c) {
      console.log('a:', a);
      console.log('b:', b);
      console.log('c:', c);
    }
    
    foo('first argument');

    However, you cannot pass values for req and res because these values are created and passed by express itself.

    Essentially you are making the mistake of calling a function where you should be passing it. router.post expects to be passed one or more functions. But you are calling parseConnections and pass its return value instead which is probably undefined.

    Here is a simple example that demonstrates the difference:

    function foo(x) {
      console.log('inside foo', 'x is ', x);
    }
    
    // bar expects to be passed a function that it can call
    function bar(callback) {
      console.log('bar received:', callback);
      try {
        callback(42);
      } catch(e) {
        console.error(e);
      }
    }
    
    
    // this will work as expected
    console.log('Passing a function');
    bar(foo); 
    
    // this will error because `bar` doesn't receive a function.
    // this is what you are doing
    console.log('Calling a function and passing its return value');
    bar(foo(21));


    One way to fix your problem is to make parseConnections return a function, which is then received by router.post. I'm using normal function declarations here so that the syntax is not too confusing:

    function parseConnections(connectionsCSVPath) {
      return function(req, res) {
         //do a bunch of stuff 
      };
    } 
    

    This requires no changes to your router.post call.


    Another solution is to pass a function to router.post that calls parseConnections instead, passing along req and res:

    router.post(
      '/upload/engagements/batch',
      checkIfAuthenticated,
      (req, res) => parseConnections('./tmp/connections.csv', req, res),
      // alternatively you can use `.bind`:
      // parseConnections.bind(null, './tmp/connections.csv'),
      parseMessages('./tmp/messages.csv'), // <- this is likely wrong as well,
                                           // but I leave this to you to figure out
      (req, res) => { 
        //do a bunch of stuff 
      }
    );