express.js routing behavior - node.js

I am beginning with express.js and I am wondering why the following code:
const express = require('express');
const app = express();
app.use('/', (req, res, next) => {
console.log('First middleware');
next();
});
app.use('/add-product', (req, res, next) => {
console.log('Second middleware');
res.send('<h1>The add-product page!</h1>');
});
app.use('/', (req, res, next) => {
console.log('Third middleware');
res.send('<h1>Hello from Express!</h1>');
});
app.listen(3000);
..when called with localhost:3000/add-product from chrome/firefox etc, produces
First middleware
Second middleware
First middleware
Third middleware
in the console?

First off, the browser is making two requests to your server:
/add-product
/favicon.ico
Then, to understand the routing, you have to understand the difference in route matching between app.use() and app.get().
app.get() matches only if the route definition matches the whole requested URL. The same is true for app.post(), app.put(), app.delete() and app.all().
app.use() matches if the route definition is at least partial match to the requested URL.
Note also that app.get() matches only GET requests while app.use() matches any HTTP verb (including POST, PUT, DELETE) though this difference doesn't matter in this particular example since both the requests happening here are GET requests.
In fact, the difference between app.use() and app.all() (who both match any HTTP verb) is that app.all() needs a full match and app.use() accepts only a partial match.
Also, the routes are checked for matches in the order specified in your code.
The /add-product routing
So, when the browser requests /add-product, you get this:
app.use('/', ...) matches because / is a partial match to /add-product.
app.use('/add-product', ...) matches because /add-product is a match to /add-product
The /add-product route then sends a response and does not call next() so routing is done for that request and the third route is not checked at all.
The /favicon.ico routing
Then, when the browser requests /favicon.ico, you get this:
app.use('/', ...) matches because / is a partial match to /favicon.ico.
app.use('/add-product', ...) does not match because it is not at least a partial match for /favicon.ico
Then, it checks your third route this time and it is a partial match for app.use('/', ...) which sends a response and doesn't call next() so routing is done for that request.
Note: It's important to pick app.get() or app.use() appropriately.

Two logs at the end - First and Third is for favicon.ico request. When you return a html string at /add-product, browse will auto makes a request like GET favicon.ico.
If you want to prevent it, at second middleware, return a html string like:
res.send('<head><link rel="icon" href="data:,"></head><h1>The add-product page!</h1>');

your browser is making two network calls: one to /add-product, and one to /favicon.ico. verify this by examining the network activity tab of your browser's dev tools, and/or make the same request from the command line (with wget or similar) and see what happens.
the first request passes through your first and second middleware functions, and the second request passes through your first and third middleware functions.

Related

NodeJs/Express: Authorise all endpoints except one

In my NodeJs/express based application, I am authorizing calls to all the endpoints by using the following middleware.
app.use(restrictByCookieMiddleware);
I want to authorize all endpoints except one i.e. I don't want "restrictByCookieMiddleware" middleware to run for "/metrics" endpoint. Is there a way to escape one endpoint?
Here, I found some examples that matches endpoint for which middleware should run, I am looking for a solution that skips one.
Your have a couple of choices:
First, you can just define the one exception route handler BEFORE the middleware. Then, it will handle that route and the routing will never get to the middleware.
app.get("/login", (req, res) => {
// handle that one special route here
});
// all other routes will get this middleware
app.use(restrictByCookieMiddleware);
Second, you can make a wrapper for the middleware that compares to the one specific route and skips the middleware if it's that route:
app.use((req, res, next) => {
// shortcircuit the /login path so it doesn't call the middleware
if (req.path === "/login") {
next();
} else {
restrictByCookieMiddleware(req, res, next);
}
});
// then, somewhere else in your code would be the /login route
app.get("/login", ...);
Third, if you have multiple routes that you want to skip the middleware for, you can segment things by router. Create a router for the non-middleware routes and put all of them on that router. Hook that router into the app object first.
Then, create a second router that contains the middleware and has all your other routes on it.
Place that specific route, you want to exclude, before this line:
app.use(restrictByCookieMiddleware);
So this will solve your problem.

MEAN: can I register more than a mini router app in app.use()?

In my node/express app.js main file, I have created a mini-app router:
var router = express.Router();
that I am passing inside to my controller functions and exporting again, at the end, I am registering the router in
app.use('/Link', router);
I now wanted to set up a second Controller folder with extra Controller function and routes only for my Angular NGX-Charts, where I prep up my data from mongoDB in correct format. Therefore, I wanted to create a second router object where I am passing and registering the right routes and middleware for that router object.
My question now is, can I create and register more than one router object for my express instance, like app.use('/Link',router1, router2, router3,...) ?
and does it behave the same like one router object then (I mean, will it find the appropriate router according to which routes I am navigating to in my browser and execute the correct middleware)?
Sure, you can do that. Common use-cases would be password-protection, generating auth tokens, parsing payloads, etc.
app.use accepts any number of "middlewares" after the first argument.
Check the docs for more details: https://expressjs.com/en/4x/api.html#app.use
The arguments are fairly flexible, there are a number of options for what you can pass.
A middleware function.
A series of middleware functions (separated by commas).
An array of middleware functions.
A combination of all of the above.
Each function gets 3 arguments, which are the Request, Response, and next callback function. Here's an example with an inline middleware that logs something and forwards to the next handler.
app.use('/secret-stuff', authorize, (req, res, next) => {
console.log('token from auth middleware', req.authToken)
next()
}, render)
One thing to note is that you can only send one response, so only the final handler would send the response to the user. Everything before that should call next() to activate the next middleware in the chain.
You could pass a number of routers as long as you make sure to forward (call next()) when the paths are unmatched. You would need to use some kind of path pattern that would allow for the middleware routers to handle greater specificity in the path (e.g. '/Link/*'), otherwise you wouldn't be able to define any sub-path handlers in the middleware routers.
In the past, I haven't had the need for sub-routers. Middleware works fine for modularization.

nodejs express wildcard route not working

I'm doing an API in nodejs with express as router.
Now i'm trying to implement an client-id and an apikey to add some security to the API, and the problem that i'm facing is the next:
One of my API call is like this:
router.get("roles/get-objects/:mail/:filter*?")
So this means, that i can request an object like this:
/roles/get-objects/mail#mail.com/customer
Now the tricky part begins... when I needed to stablish a middleware to read an client-id and an apikey to verify that the client is authorized to se the API, so I did this:
In the declaration of the middleware, I use this wildcard:
router.all('/*', function (req, res, next) {
XXXX})
The thing is, I have tried in the middleware, as a wildcard everything...
I want that any API call is filtered thru that middleware, but apparently I can't find the right wildcard for it...
When I use /roles/* as wildcard, if I do a request to /roles it does work, but when I use the complete URL like: /roles/get-objects/mail#mail.com/customer it doesn't go thru my middleware.
So anybody has any idea? i'm starting to loose my mind
Thank you so much to all of you!
EDIT:
Now i'm using this middleware declaration:
router.use(function (req, res, next) {XXXX})
So when I call:
/roles/get-objects/
It's executed, the problem is when I add the email to the route:
/roles/get-objects/mail#mail.com
The app goes directly to the route that i have for that, but omits my middleware:
router.get("roles/get-objects/:mail",
I don't understand why is this happening, apparently everything should go thru my middleware first, or am I wrong?
If you want to establish a middleware to check all HTTP request whose URL starting with /roles/, the middleware should be placed before any other specific router definition:
router.use('/roles', function(req, res, next) {...});
...
router.get('/roles/get-objects/:mail', ...);
If the middleware is defined after specific route, when HTTP request comes in, the specific route is targeted and processed, the middleware won't be executed any more:
router.get('/roles/get-objects/:mail', ...);
...
router.use('/roles', function(req, res, next) {...}); // This middleware logic won't execute when request is sent to '/roles/get-objects/some-email', as the request has already been handled and response is already sent to browser.

Nodejs Express keeps serving the same file to all requests

So i have an express app and in the app.js i have this:
app.use('/index', function (req, res, next){
res.sendFile(__dirname+'/index.html');
}
app.get('/script.js',function(req,res){
res.sendFile(__dirname+'/public/script.js');
and after starting the server and type localhost:3000/index and the app works fine but here comes the problem..
when i change the first app.use() function to:
app.use('/', function (req, res, next){}
so that i don't have to type the index part in the URL, all the next get requests respond with index.html page and i tried adding
res.end(); after res.sendFile();
but no other response gets sent after that, how can i solve?
Your / route is acting as a wildcard and capturing all requests, which means that anything not matching a route defined BEFORE this route will be caught by it. You have two options here:
Change app.use to app.get so that you are explicitly only matching / (and only with a GET method)
Move the route to the bottom of all of your routes
As explained on the Express.js API docs for app.use here:
A route will match any path, which follows its path immediately with a “/”. For example: app.use('/apple', ...) will match “/apple”, “/apple/images”, “/apple/images/news”, and so on.

Routes chaining in express

I have a simple app, built with the help of node.js and express. This app has multiple routes and basic login/logout mechanics. I want all routes to redirect to a login form if the user is NOT logged in. This can be done by inserting auth checker lines into each route file.
The question is :
Is it possible to chain the routes to push all requests through login checker route and then pass it to the requested one without writing anything to existing route files?
E.g. existing routes are "/", "/upload", "/login", "/logout".
I want the request to get or post "/upload" to be first processed by "/login" route and then by "upload" route.
Yes, you can chain handlers in a route. Your handler definition should be like
routehandler(req, res, next){
//send response directly
//or call next to call the next handler
}
You can then put multiple handlers in the order you want:
app.get('/server', ensureAuthenticated, housekeeping, routes.server.get)
Here ensureAuthenticated and housekeeping dont send the response just call next(). The last one returns the resulting page.
Please see you would want a different handler than what you use for login. Login page would do authentication, rest pages should just check if the user is authenticated or not. The difference would be clear if you are using sessions.
The args for the route handlers are the same as that of middlewares. Those are :
(err, req, res, next) Error handlers
(req, res, next) Non-error handlers
A trivial variation of above is that next can be left out if it is the end function you want in callback chain. You cannot have other args apart from these. You can see the how they are called here (line 154).
A route consists of method, route-match and callback array. Like the middlewares the callback chain is executed sequentially for a specific route until response is returned or error is thrown.

Resources