When to use next() and return next() in Node.js

node.jsExpressConnectV8

node.js Problem Overview


Scenario: Consider the following is the part of code from a node web app.

app.get('/users/:id?', function(req, res, next){
    var id = req.params.id;
    if (id) {
        // do something
    } else {
        next(); //or return next();
    }
});

Issue: I am checking which one to go with just next() or return next(). Above sample code works exactly the same for both & did not show any difference in execution.

Question: Can some one put light on this, when to use next() and when to use return next() and some important difference?

node.js Solutions


Solution 1 - node.js

As @Laurent Perrin's answer:

> If you don't do it, you risk triggering the callback a second time later, which usually has devastating results

I give an example here if you write middleware like this:

app.use((req, res, next) => {
  console.log('This is a middleware')
  next()
  console.log('This is first-half middleware')
})

app.use((req, res, next) => {
  console.log('This is second middleware')
  next()
})

app.use((req, res, next) => {
  console.log('This is third middleware')
  next()
})

You will find out that the output in console is:

This is a middleware
This is second middleware
This is third middleware
This is first-half middleware

That is, it runs the code below next() after all middleware function finished.

However, if you use return next(), it will jump out the callback immediately and the code below return next() in the callback will be unreachable.

Solution 2 - node.js

Some people always write return next() is to ensure that the execution stops after triggering the callback.

If you don't do it, you risk triggering the callback a second time later, which usually has devastating results. Your code is fine as it is, but I would rewrite it as:

app.get('/users/:id?', function(req, res, next){
    var id = req.params.id;

    if(!id)
        return next();

    // do something
});

It saves me an indentation level, and when I read the code again later, I'm sure there is no way next is called twice.

Solution 3 - node.js

next() is part of connect middleware. Callbacks for router flow doesn't care if you return anything from your functions, so return next() and next(); return; is basically the same.

In case you want to stop the flow of functions you can use next(err) like the following

app.get('/user/:id?', 
    function(req, res, next) { 
        console.log('function one');
        if ( !req.params.id ) 
            next('No ID'); // This will return error
        else   
            next(); // This will continue to function 2
    },
    function(req, res) { 
        console.log('function two'); 
    }
);

Pretty much next() is used for extending the middleware of your requests.

Solution 4 - node.js

The difference between next() and return next() is very simple as another programming principle. Some lines of code are explain below:

    app.use((req, res, next) => {
       console.log('Calling first middleware');
       next();
       console.log('Calling after the next() function');
    });


    app.use((req, res, next) => {
       console.log('Calling second middleware');
       return next(); // It returns the function block immediately and call next() function so the return next(); and next(); return; are the same
       console.log('After calling return next()');
    });

Output is

> Calling first middleware
> Calling after the next() function
> Calling second middleware

Solution 5 - node.js

import express from "express"
  
const app = express()
// API for the testing of next() 
app.get(
  '/next', function (req,res,next) { 
    console.log('hi there ');
    next();
    console.log('you are still here');
  }
)
  
// API for the testing of return next() 
app.get(
  '/return-next', function (req,res,next) { 
    console.log('hi there');
    return next(); 
    console.log('you are still here');
  }
)
  
app.listen(5000,()=> {
  console.log("App is running on port 5000")
})

next() in /**next** route will call the middlewares and after the middlewares are executed it comes back to where it is called (same like a function call) and executes the rest of the codes

output:

hi there

you are still here

In /**return-next** route there is a return in front of next() which returns the controller

output:

hi there 

if you consider next() like a function call you could understand it properly

Solution 6 - node.js

It is best not to use it at all! I explain, and that is what I do also explain it.

The next () function that can have any name and by convention has been set to next. It is indirectly related to the operations (PUT, GET, DELETE, ...) that are generally performed on the same URI resource for example / user /: id

app.get('/user/:id', function (req,res,next)...)
app.put('/user/:id', function (req,res,next)...)
app.delete('/user/:id', function (req,res,next)...)
app.post('/user/', function ()...)

Now if you look at app.get, app.put and app.delete use the same uri (/ user /: id), the only thing that differentiates them is their implementation. When the request is made (req) express puts the req first in app.get, if any validation you created because that request is not for that controller fails, it passes the req to app.put which is next route in te file and so on. As seen in the example below.

    app.get('/user/:id', function (req,res,next){
    
    if(req.method === 'GET')
    //whatever you are going to do
    else
      return next() //it passes the request to app.put

    //Where would GET response 404 go, here? or in the next one. 
    // Will the GET answer be handled by a PUT? Something is wrong here.
    
   })
    app.put('/user/:id', function (req,res,next){
    
    if(req.method === 'PUT')
    //whatever you are going to do
    else
      return next()
    
   })

The problem lies, that in the end you end up passing the req to all the controllers hoping that there is one that does what you want, through the validation of the req. In the end all controllers end up receiving something that is not for them :(.

#So, how to avoid the problem of next ()?

The answer is really simple.

1-there should only be one uri to identify a resource

http://IpServidor/colection/:resource/colection/:resource if your URI is longer than that, you should consider creating a new uri

Example http://IpServidor/users/pepe/contacts/contacto1

2-All operations on this resource must be done respecting the idempotence of the verbs http (get, post, put, delete, ...) so the call to a URI really only has one way of calling

POST http://IpServidor/users/  //create a pepe user 
GET http://IpServidor/users/pepe  //user pepe returns   
PUT http://IpServidor/users/pepe  //update the user pepe 
DELETE http://IpServidor/users/pepe  //remove the user pepe

More info [https://docs.microsoft.com/es-es/azure/architecture/best-practices/api-design#organize-the-api-around-resources][1]

#Let's see the code! The concrete implementation that makes us avoid the use of next ()!

In the file index.js

//index.js the entry point to the application also caller app.js
const express = require('express');
const app = express();

const usersRoute = require('./src/route/usersRoute.js');

app.use('/users', usersRoute );

In the file usersRoute.js

    //usersRoute.js
    const express = require('express');
    const router = express.Router();

    const getUsersController = require('../Controllers/getUsersController.js');
    const deleteUsersController = require('../Controllers/deleteUsersController.js');

    router.use('/:name', function (req, res) //The path is in /users/:name
    {
    switch (req.method)
    {
    case 'DELETE':
      deleteUsersController(req, res);
      break;
    case 'PUT':
     // call to putUsersController(req, res);
     break;
    case 'GET':
     getUsersController(req, res);
     break;
    default:
     res.status(400).send('Bad request');
    } });

router.post('/',function (req,res) //The path is in /users/
{
    postUsersController(req, res);
});

module.exports = router;

Now the usersRoute.js file does what a file called usersRoute is expected to do, which is to manage the routes of the URI /users/

//file getUsersController.js

//getUsersController.js
    const findUser= require('../Aplication/findUser.js');
    const usersRepository = require('../Infraestructure/usersRepository.js');

    const getUsersController = async function (req, res)
    {
        
       try{
          const userName = req.params.name;
        //...
          res.status(200).send(user.propertys())
    
        }catch(findUserError){
           res.status(findUserError.code).send(findUserError.message)
        }
    }
   module.exports = getUsersController;

In this way you avoid the use of next, you decouple the code, you gain in performance, you develop SOLID, you leave the door open for a possible migration to microservices and above all, it is easy to read by a programmer.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAmol M KulkarniView Question on Stackoverflow
Solution 1 - node.jsPJCHENderView Answer on Stackoverflow
Solution 2 - node.jsLaurent PerrinView Answer on Stackoverflow
Solution 3 - node.jsdrinchevView Answer on Stackoverflow
Solution 4 - node.jsRony AhmmodView Answer on Stackoverflow
Solution 5 - node.jsVaisakh GopinathView Answer on Stackoverflow
Solution 6 - node.jsDavidView Answer on Stackoverflow