How is it that a callback function can successfully execute with fewer arguments provided?

Code

I'm really intrigued by how this code functions. In the verifyUser function, it passes a callback function as the fourth argument to the verifyToken function. However, upon examining the verifyToken function itself, I noticed that it only has three parameters/arguments. How is this possible?

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!"));
    }
    
    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!"));
        }
    });
};

Answer №1

After delving into the depths of Express's source code, I unearthed this insightful documentation

Upon invoking the next() function, it behaves like middleware - proceeding to execute the route or any subsequent parameter functions.

Source

It appears that next() first attempts to call a callback argument if one is provided, before moving on to invoke the next middleware.

This design choice seems intended to allow for chaining a call to verifyToken before verifyUser, maintaining the flow without altering the core implementation of next() which would otherwise jump to the next middleware when called within verifyToken, bypassing verifyUser.

I crafted a sample script that demonstrates this concept:

function printResult(object, next) {
  console.log("The final result 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)

It's worth noting that the presence of the anonymous function check caller.name !== '' is necessary due to an error thrown when checking the arguments property of an anonymous function.

A more in-depth explanation of why this feature matters.

If your app follows this sequence, everything works as expected:

  • Middleware 1
    • next() proceeds to next middleware
  • verifyToken (Middleware 2)
    • next() moves to next middleware
  • Middleware 3

However, in a scenario where next() doesn't trigger the parameter function:

  • Middleware 1
    • next() advances to next middleware
  • verifyUser (Middleware 2)
    • invokes verifyToken
      • next() inside verifyToken skips ahead to next middleware <- This causes an issue
    • The execution flow never reaches verifyUser <- Due to this skipped step
    • next() continues to next middleware
  • Middleware 3

To address this, they enable next() to call a parameter function:

  • Middleware 1
    • next() progresses to next middleware
  • verifyUser (Middleware 2)
    • triggers verifyToken with verifyUser's logic embedded as a callback verifyCB (merely an example alias)
      • next() in verifyToken locates and invokes the callback, namely verifyCB
    • next() in verifyCB proceeds to next middleware
  • Middleware 3

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Avoid Updating State After Adding Row in material-table

When utilizing the material-table library, my goal is to display a different component upon adding a row. Although the code functions as expected, I encountered the following error in the console: Warning: Can't perform a React state update on an unm ...

Convert millimeters to inches with a unique AngularJS filter that activates on click

I am currently working on a UI that requires users to enter dimensions for width and height. Within the UI, there are 2 buttons available - one for 'mm' and the other for 'inches'. When either of these buttons is pressed, the active cl ...

Implementing ajax functionality for a form component in vuejs

I am currently working with a Vue.js component that serves as a form with a single field for an email input. The default value of this field, "email", is provided to the component by the parent as a prop. Upon form submission, I need to trigger an event to ...

Error: Unable to access 'push' property of null object in Next.js Link

Utilizing Vite to develop a reusable component has led to an error upon publishing and reusing it: TypeError: Cannot read properties of null (reading 'push') The code for the component is as follows: import React from "react"; import ...

Expanding and collapsing UI elements within an ngRepeat loop in AngularJS

I have been attempting to incorporate collapsible panels within an ngRepeat loop. Below is the code I have been using: <div class="panel panel-default" ng-repeat="element in elements"> <div class="panel-heading"> ...

Using JQuery to reverse the action of hiding an image

Having some trouble with my Javascript and JQuery skills, so bear with me. I've set up a gallery of images where users can drag and drop them into a designated drop zone. Everything works fine when dragging an image from the gallery to the drop zone a ...

displaying issue: Issue [ERR_HTTP_HEADERS_SENT]: Unable to modify headers after they have been sent to the recipient in the command line

Node.js version: v18.15.0 mongoose: 7.1.1 After successfully sending data through PostMan and saving it without any issues, I encountered an error on the console. app.post('/product', async (req, res) => { try { const singleProduc ...

What is the best choice for code design patterns in a nodejs environment? What are the key considerations for creating a well-

Although I have a background in games development using C/C++/C#, I have recently delved into automated testing and now I am eager to learn more about backend development. My current project involves creating a platform for automated backups, building fr ...

Error encountered: Unspecified "from" address in the provided or default options

Seeking guidance on a project related to Ethereum and Solidity, part of Udemy's course titled "Ethereum and Solidity: The Complete Developers Guide." I am currently working on building the front-end for a Kickstarter alternative. I am facing an issue ...

Endless scrolling feature malfunctioning

On my profile_page.php, the default setting is to display 10 posts (10 rows from the database) for the user. If a user has more than 10 posts, and scrolls to the bottom of the page, the div should expand to reveal the remaining posts (maximum of 10). For e ...

Enhance the current model in backbone.js by incorporating additional data

When a user selects an item on the webpage, more details need to be fetched and displayed. The API function /api/full_details has been implemented to return the additional data for that item. Challenge: How can I retrieve the additional data and append it ...

Explore the associative array within a JSON using jQuery to extract and manipulate the values within the array

I'm working with a JSON file containing surnames and first names in an array, along with other objects. How can I specifically extract the names "Jhon" and "Jason"? Below is a snippet from my JSON file: [{ "surname": "Vlad", "first_name": [ ...

What is the best way to attach a Label to a THREE.Mesh object?

I'm looking to show the name of a Three.js Three.Mesh as a label when hovering over the mesh. Does anyone know how to achieve this in Three.js? Could someone provide an example code snippet for this? ...

jQuery tabs become non-functional following submission of form

I have a form contained within a jQuery tab div that is loaded using AJAX: <div id="tabs-2"> <form id="frmLogin" name="frmLogin" class="cmxform" method="post" action="actions/login.php"> <p> <label>Username& ...

Node.js dynamically updates exports for efficient code execution

I am facing an issue with exporting and importing an object in my code. The object starts off empty but gets populated with keys and values when a button is clicked. However, when other files import this object, it still shows as empty. How can I dynamical ...

Verifying the accessibility of a website using JQuery/Javascript

I am attempting to use JavaScript to ping a website and display the result. Unfortunately, I have not had any success with this JSFiddle: https://jsfiddle.net/yyjowtru/ Although I believe I am close to achieving the desired outcome, changing the URL in t ...

Combine the values in the array with the input

I received some data from the back-end which is being written to a form, and it's in the form of an array of objects Below is the code snippet: this.companyDetailsForm = new FormGroup({ directors : new FormControl(response?.companyDirectors) ...

The session storage system does not retain any user data

I am currently working on developing REST APIs using Express, MongoJS, and Connect-Mongo. I'm facing an issue with storing data in sessions: var express = require('express'), mongojs = require("mongojs"), MongoStore = require('connect ...

Is it possible to pass arguments to setTimeout function?

At the current moment, my code looks like this: function showGrowl(lastNumber) { var num = lastNumber; //keep generating a random number untill it is not the same as the lastNumber used while((num = Math.ceil(Math.random() * 3)) == lastNumber); ...

Exploring JSON data structures using autocomplete functionalities

Here's the code I'm working with: <s:hidden id="s" value="%{Users}"/> The variable Users contains an array list of User objects. This code is written in Javascript. I want to access Users as JSON for auto-complete functionality: var valu ...