Why does callback function work when there are fewer arguments?

Issue

Code

Can someone please explain why this code works when in the function verifyUser, the function verifyToken is passed a callback function as the fourth argument. But if we look at the verifyToken function, it only has 3 parameters/arguments. How does this work????

import jwt from "jsonwebtoken";
import { createError } from "../utils/error.js";

export const verifyToken = (req, res, next) => {
    const token = req.cookies.access_token;
    if (!token) {
        return next(createError(401, "You are not authenticated!"));
    }
    //Verifying if token is correct
    //Error (err) or token data (user) are returned by verify function
    jwt.verify(token, process.env.JWT, (err, user) => {
        if (err) return next(createError(403, "Token is not valid!"));
        req.user = user;
        next();
    });
};

export const verifyUser = (req, res, next) => {
    verifyToken(req, res, next, () => {
        if (req.user.id === req.params.id || req.user.isAdmin) {
            next();
        } else {
            return next(createError(403, "You are not authorized!"));
        }
    });
};

Solution

I went digging in Express’s source code and found this documentation

Once the next() function is invoked, just like middleware
it will continue on to execute the route, or subsequent parameter functions.

Source

So it looks like next() first tries to call a callback argument if it it’s passed, then tries to call the next middleware.

I think the reason they did this, is to hardcode or chain a call to verifyToken before verifyUser without changing their implementation of next() which would jump to the next middleware when called in verifyToken instead of continuing verifyUser.

I wrote an example script that uses the same idea here:

function printResult(object, next) {
  console.log("The final results is: " + object.result);
  next();
}

function printLog(object, next){
  console.log('Running printLog')
  console.log('The current result is: ' + object.result)
  next();
}

function add5(object, next) {
  // Chain printLog before add5 so that it always runs before it
  printLog(object, next, () => {
    console.log('Now running add5')
    object.result = object.result + 5;
    next();
  })
}


function addOne(object, next) {
  console.log('Running addOne')
  object.result = object.result + 1;
  next();
}

function callStack(stack, object) {

  function next(index, object) {
    return function runNext() {
       let caller = runNext.caller
       if(caller.name !== '' && caller.arguments[2]) {
          caller.arguments[2]()
       } else if (stack[index + 1]){
          stack[index + 1](object, next(index + 1, object));
       }
     }  
   }
   
  stack[0](object, next(0, object)); 
}

const stack = [addOne, add5, printResult]
const object = {result: 0}

callStack(stack, object)

Note that the anonymous function check caller.name !== '' is there because checking the arguments property of an anonymous function throws an error.

More thorough explanation of why this is important.

So if you’re app is like this there’s no problem:

  • Middleware 1
    • next() goes to next middleware
  • verifyToken (Middleware 2)
    • next() goes to next middleware
  • Middleware 3

But if it was like this and next() didn’t call the parameter function:

  • Middleware 1
    • next() goes to next middleware
  • verifyUser (Middleware 2)
    • calls verifyToken
      • next() in verifyToken goes to next middleware <- This is an issue
    • Continue verifyUser <- Because this never happens
    • next() goes to next middleware
  • Middleware 3

So they let next() call a parameter function:

  • Middleware 1
    • next() goes to next middleware
  • verifyUser (Middleware 2)
    • calls verifyToken with verifyUser‘s implementation as a callback veryifyCB (jus an alias for th example)
      • next() in verifyToken checks for a callback, finds verifyCB and calls it
    • next() in verifyCB goes to next middleware
  • Middleware 3

Answered By – Brother58697

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published