Express 4 router.param doesnt fire - node.js

I have a node express 4 app and I want to mount a route (contacts) on a parent route. i.e
/:fundid/contacts
In my fund route I declare
var router = require('express').Router({ mergeParams: true });
var contactRoutes = require('./contacts');
router.use('/:fundid/contacts', contactRoutes);
In my contact route
var router = require('express').Router({ mergeParams: true });
router.param('fundid', function(res, req, next, id){});
The problem is that this param call does not fire. From what I can garner from the documentation these param calls are relative to the router they are declared on, but i would have thought mergeParams:true would affect this, but it doesn't. The route is otherwise working, and both routes are called. Am I missing something?
The reason I want to do this is because I want to mount the contacts route on multiple parent routes, and build a filter based on those parent parameters

This comment suggests that parameters are tied to the router they are declared with; so in your case, fundid can only be handled by the "fund" router. mergeParams serves a different purpose, namely to provide access to req.params.fundid from child routers.
You can always use a request middleware in your contact router to perform special operations based on the fundid, though:
router.use(function(req, res, next) {
var id = req.params.fundid;
...
});

Related

what happens in app.use(express.static) and app.use(require("cors")()) and what middlewares are

I started with express a few days ago.
I dont really understand what happens in:
const express = require("express")
const app = express()
app.use(express.static(path.join(), "public"))
app.use(require("cors")())
app.listen(3000, () => console.log("running"))
the first example worked for me but i dont really understand it.
and basiclly i dont understand what happens in app.use() and what middlewares are..
someone can help me pls?
i read many blogs and I didnt got it :(
The Background
There are several parts to explaining this. First, off app.use() expects a middleware function to be passed to it. That would be a function with a signature like this:
app.use(function(req, res, next) {
console.log(req.path); // log incoming request path
next(); // continue routing to other handlers
});
It accepts other combinations of parameters, including an initial path and you can pass multiple middleware functions too and it will chain them together, but the basics of your question is about a single middleware function as shown above. That middleware function gets three arguments req - the incoming request object, res - the outgoing response objet and next - a function to call if you want to continue routing or report an error.
The job of one of these middleware function is to use the input in the req object to do some kind of processing of that input (depending upon what the purpose of the middleware function is) and then do one of three things:
Send a response using something like res.send() in which case the request has been handled and a response has been sent and no further routing will be done.
Continue routing to further request handlers in the chain by calling next().
Abort routing and go to the Express error handler by calling next(err).
The express.static() Middleware
So, that's what is expected of a function passed to app.use(). Now, let's look at the two examples you ask about. Let's start with express.static():
app.use(express.static(path.join(), "public"))
First, this isn't proper use of express.static(). I'm not sure exactly what you intended, but I will assume you meant something like this:
app.use(express.static(path.join(__dirname, "public")));
In this case, express.static() takes some configuration information which is the resulting path from calling path.join(__dirname, "public") and uses that to create a custom middleware function. So, calling express.static(path.join(__dirname, "public")) returns a function that expects to be called with the three middleware arguments we previously discussed. It is logically identical to this:
const publicPath = path.join(__dirname, "public");
const myStaticMiddleware = express.static(publicPath);
app.use(myStaticMiddleware);
which is logically equivalent to this:
const publicPath = path.join(__dirname, "public");
const myStaticMiddleware = express.static(publicPath);
app.use(function(req, res, next) {
myStaticMiddleware(req, res, next);
});
Where the code has been broken down into separate steps just so you can see each step separately.
And, in case you didn't already know, the point of the express.static() middleware is to serve static files from a designated directory if an incoming request matches a filename in that designated directory exactly and has an appropriate file type.
The cors Middleware
For your second example:
app.use(require("cors")())
Let's again break that down to the individual steps:
const cors = require("cors"); // load cors module
const corsMiddleware = cors(); // create cors middleware function
app.use(corsMiddleware); // register middleware with Express server
Which can be expanded to:
const cors = require("cors");
const corsMiddleware = cors();
app.use(function(req, res, next) {
corsMiddleware(req, res, next);
});
Just to show you that corsMiddleware is called with these three arguments.
The purpose of this particular middleware is to help configure a response to this request so that cross origin requests will be accepted.

How do I access URL parameter after i've routed my express app to another file?

My main express server is called app.js in Node.js.
app.use("/login", require(./routes/login));
app.use("/:id", require("./routes/users"));
When I try to access the URL parameter, it returns undefined.
I tried logging req.params:
const express = require('express');
const router = express.Router();
router.get('/dashboard', (req, res) => {
res.send(`Current Ornament Status and Data for ${req.params}`);
});
module.exports = router;
It gives me an empty array.
I suppose that it the parameter is inaccessible in another file after routing. Could you suggest a workaround?
I think you're missing some fundamental bits about express.js routing for this to make sense.
The requires line means it is loading another piece of code. So you need to show us that too.
The :id thing requires a longer explanation.
Let's say I want the server to process the URL /finduser/23
Where 23 can vary, could be just about any number. I am NOT going to write 99 different versions of router.get, right?
router.get("/finduser/1",...
router.get("/finduser/2",...
router.get("/finduser/3",...
No, what we do is turn that into a parameter
router.get("/finduser/:id",...
Then whatever number we pass turns into req.params.id, assume router passes req,res
EX: If we pass URL /finderuser/15, then req.params.id = 15
If you just pass /finduser then req.params.id gets NOTHING.
Full details are available here
http://expressjs.com/en/guide/routing.html#route-parameters
Your example:
router.get('/dashboard', (req, res)
Doesn't have ANY parameters. so req.params.id has nothing.

How can I set the routes correctly?

Here is a sample code:
var x = require('./folder/usefile');
var Auth = passport.authenticate('jwt', { session: false });
module.exports = function(app){
console.log('inside function(app)'); //line 1 executed!
var player = express.Router();
var finalRun = express.Router();
app.use('/api/usefile',player);
player.get('/', Auth, x.login);
player.post('/post', Auth, function(req, res){
x.register});
app.use('/api',finalRun);
console.log('inside api'); //line 2 is executed!
}
In usefile my login function is present which is exported.
I am using passport here, whose functionality is present in separate file here it is used only for authentication.
When I ran http://localhost:8080/api/usefile/---> for get method, it is displaying 404. same for post method.
The console is printing line 1 and line 2(have mentioned in the comments) and the flow is line1, line2.
Can anyone help me to find what mistake I have made here?
You've created two Router instances player and finalRun. There are two main issues:
This line player.use('/usefile',player) should be app.use('/usefile',player). express.Router().use is used to add a middleware for all routes registered to the router (in this case player). For example, if you wanted to use the Auth middleware from Passport for every player route, you could declare it as player.use(Auth).
You are not declaring any route handling for the finalRun router. So in this case, your route handlers registered with player will handle host/usefile and finalRun will handle any host/api.
If you are wanting the handlers registered with player to handle /api/usefile, using a single router instance would be far simpler.
Register the /api/usefile path with the player router like:
app.use('/api/usefile', player);
Docs for app.use
Docs for router.use

How to use single piece of middleware with more than one express router?

I am working on a university project and we have decided to go for MEAN technology stack. To be honest I am a beginner with NodeJS and express, more precisely this is the first time I do sth with it.
I've found that is preferable to use express.Router rather than putting all routes to express instance e.g. app.post('path', function(req, res) { ... })
So this is what I have
var express = require('express');
var app = express();
function authorizationMiddleware(req, res, next) {
...
}
// handles login, doesn't meed autorizationMiddleware
var authRouter = express.Router();
authRouter.route('/login')
.post(function (req, res) {
...
});
// handles fetching of a single, all person(s), fetching of transactions for a person
var personRouter = require('./routes/personRoutes')(Person, Transaction, autorizationMiddleware);
//handles adding of a new transaction e.g. POST /api/transactions where params such as sender, recipient and amount are passed in body
var transactionRouther = require('./routes/transactionRoutes')(Person, Transaction, autorizationMiddleware);
app.use('/api', authRouter);
app.use('/api/persons', personRouter);
app.use('/api/transactions', transactionRoutes);
app.listen(8080, function () {
console.log('Listening on port: ' + 8080);
});
As you can see I have three routers (not even sure if I have gonne too far with them), authRouter is handling login only and I have also decided to separate persons logic from transactions logic too. (maybe I could have handled creation of new transaction in a way like /api/persons/:personId/transactions but I rather liked the idea of sending all required params in body).
I would like to ask if you agree with the solution I tried. As you can see I am passing authrizationMiddleware function (handles verification of JWT token) function to router modules and using it there.
Is there maybe a better way to use the same middleware with of multiple routers or is this a legit way?
Thx in advance
Cheers!
I don't get why you use 3 Routers. The "common" way to go (or at least the way I go) is to put all the routes in the same place, except when the path is very different or the purpose is different (for example I separate the error routes from the others).
For example, let's say I need to build a rest api for an app, I would probably have paths like:
/users/:userid
/users/:userid/comments/:commentid
/locations
...
All these routes can go in the same Router and if you want, you can apply specific authentication/authorization middlewares to them:
router.get("/users/:userid",
doAuthentication, authorizeOnUserId,
userController.getUserById);
router.get("/locations",
doAuthentication, authorizeLocations,
locationController.getAllLocations);
The middlewares are called in sequence and the request is passed on to the next middleware only if there are no errors (unauthenticaed/ unhauthorized).
Then you can simply import your routes like this:
app.use('/api', router);
Using this technique allows you to have a fine grain control over your routes.
Hope this helps.

404 when accessing new route

I'm trying to add a new route (/profile) to my NodeJS Express web application. I've modified my app.js file like this:
var routes = require('./routes/index');
var profile = require('./routes/profile');
app.use('/', routes);
app.use('/profile', profile);
The '/' index path works fine, my issue is with '/profile'. Whenever I try to access it, I get a 404. This is profile.js:
var express = require('express');
var router = express.Router();
router.get('/profile', function(req, res) {
var username = req.session.username;
if(username) {
res.render('profile');
} else {
res.redirect('/login');
}
});
module.exports = router;
I don't understand what I'm doing wrong because in the example express application that is generated, '/users' works fine. I basically copied that format, but it's throwing a 404. Any ideas?
In my profile.js, I had to change my GET request path to this:
router.get('/', function(req, res) {
//code
});
Otherwise, the router would be looking for /profile/profile. When I change it to /, it's just looking for the root of `/profile', or at least that's how I understand it.
To understand what you are doing wrong you should know that Node.js uses middleware functions to route your requests. To simplify you can think about it as a chain of functions.
Middleware is like a plumbing pipe, requests start at the first middleware you define and work their way “down” the middleware stack processing for each path they match.
So with the following statement you added a middleware function to handle any request starting with the root path /profile, and it is a common pattern in Node to use the use method to define the root paths.
app.use('/profile', profile);
The use method is doing part of the routing in your scenario and the statement above will match any route starting with that path, including /profile/all or /profile/12 or even /profile/go/deeper/inside.
However, you want to narrow down that routing to something more specific, so that is why you pass a router middleware function (profile in your case) to match more specific routes instead of all routes starting with /profile.
The profile middleware function is actually the next step in the chain of functions to execute, and it will start from the root path specified in the use statement, which is the reason why you need to start again with / and not with /profile. If you wanted to match a profile by ID you would do:
router.get('/:id', ...)
Which would be concatenated with the base URL (from the /use statement) and would match a request like /profile/2 or /profile/abc.

Resources