How do we pass parameters to a mounted route in nodeJS?

5.5k views Asked by At

I'm taking a course on NodeJS, there were a few assignments related to routing, everything works fine except this part which seems a little odd: For some reason, I cannot read the parameter ID being passed to the mounted router.

dish.js

const express = require('express');
const bodyParser = require('body-parser');
const dishRouter = express.Router();

dishRouter.use(bodyParser.json());

dishRouter.route('/')
.all((req,res,next) => {
    res.statusCode = 200;
    res.setHeader('Content-Type','text/plain');
    next();
})
.get((req,res) => {
    console.info('Info: ',req);
    res.end(`Sending details of the dish back to you: ${req.params.dishId}`);
})
.post((req,res) => {
    res.statusCode = 403;
    res.end(`Operation not supported: ${req.params.dishId}`);
})
.put((req,res) => {
    res.write(`Updating the dish...: ${req.params.dishId} \n` );
    res.end(`Will update this dish: ${req.body.name} with details: ${req.body.description}`);
})
.delete((req,res) => {
    res.end(`Deleting this dish: ${req.params.dishId}`);
});

exports.dish = dishRouter;

dishes.js

const express = require('express');
const bodyParser = require('body-parser');
const dishesRouter = express.Router();

dishesRouter.use(bodyParser.json());

dishesRouter.route('/')
.all((req,res,next) => {
    res.statusCode = 200;
    res.setHeader('Content-Type','text/plain');
    next();
})

.get((req,res) => {
    res.end('Sending all dishes back to you');
})  

.post((req,res) => {
    res.end(`Will add the dish: ${req.body.name} with details: ${req.body.description}`);
})

.put((req,res) => {
    res.statusCode = 403;
    res.end(`Operation not supported.`);
})

.delete((req,res) => {
    res.end(`Deleting all dishes.....`);
});

exports.dishes = dishesRouter;

index.js

const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const http = require('http');
const dishRouter = require('./routes/dish');
const dishesRouter = require('./routes/dishes');
const hostname = 'localhost';
const port = 3000;

const app = express();
app.use(morgan('dev'));
app.use(bodyParser.json());

app.use('/dishes',dishesRouter.dishes);
app.use('/dishes/:dishId',dishRouter.dish);

app.use(express.static(__dirname+'/public'));

app.use((req,res,next) => {
    res.statusCode = 200;
    res.setHeader('Content-Type','text/html');
    res.end('<html><body><h1>This is an Express Server</h1></body></html>');
});

const server = http.createServer(app);

server.listen(port,hostname,(req,res) => {
   console.info(`Server running on port: ${port}, at: ${hostname}`);
})

This GET localhost:3000/dishes/123 is calling the right route, but the parameter dishId comes back as "undefined". Again, just learning nodeJS, seems like my receiver/mounted route should receive those parameters just fine, the body can be read properly, but not the params. ... thanks.

2

There are 2 answers

1
Bergur On BEST ANSWER

Yeah the params don't flow between routers. You're on a new router, hence new route params object.

You can check out the code for this: https://github.com/expressjs/express/blob/master/lib/router/index.js#L43 Check out line 43 and line 53 where route.params is set to an empty object.

Some examples:

index.js

app.use('/dishes/:dishId',(req, res) => {
  console.log('now I get my dishId', req.params.dishId)
});

dish.js (version 1)

dishRouter.route('/')
.get((req, res) => {
  console.log('now i get nothing', req.params)
})

dish.js (version 2)

dishRouter.route('/:anotherId')
.get((req, res) => {
  console.log('now we get another parameter', req.params.anotherId)
})
// the path would be /dish/123/456

I'm not sure if there is a offical-expressjs-way to pass the params object between routers.

One solution would be to create a custom handler

index.js

app.use('/dishes/:dishId', handler)

handler.js

function handler (req, res, next) {
  if (req.method === 'GET') {
    console.log('now we get it', req.params)
  }
}

module.exports = handler

Anoter way would be to add the dishId to the request object before calling the router:

index.js

app.use('/dishes/:dishId', (req, res, next) => {
  req.dishId = req.params.dishId
  router(req, res, next)
})

dish.js

const express = require('express')
const router = express.Router()
  router.route('/')
  .get((req, res) => {    
    console.log('nothing here', req.params)
    console.log('dishId', req.dishId)
  })

module.exports = router

Third way would be to send the params as options to a router function

index.js

app.use('/dishes/:dishId', (req, res, next) => {
  router(req.params)(req, res, next)
})

dish.js

function createRouter (options) {
  const router = express.Router()
  router.route('/')
  .get((req, res) => {    
    console.log('nothing here', req.params)
    console.log('but alot here', options)
  })

  return router
}

module.exports = createRouter

If you want you could also just put the :dishId on the router as an optional parameter

index.js

app.use('/dishes', dishesRouter)

dishes.js

const express = require('express')
const router = express.Router()

router.route('/:dishId?')
  .get((req, res) => {        
    if (req.params.dishId) {
      res.end(`Sending details of the dish back to you: ${req.params.dishId}`)
    } else {
      res.end('Sending all dishes back to you');
    }
  })

module.exports = router
0
Brendan On

New option for Express 4.5.0+

The new option mergeParams should help with this. When set to true this will combine the params from the parent to the child router. Note that any params specified in the child router take precedent.

// index.js

const router = require('dishes')

app.use('/dishes/:dishId', router)
// dish.js

const router = new Router({ mergeParams: true })

router.get('/', (req, res) => {
    // req.params.dishId is defined here ...
})

exports.router = router