log request method on routing - node.js

For my NodeJs project I use the Express router. I created a router.js
module.exports = (app) => {
app.use('/', require('./routes/home.js'));
app.use('/contact', require('./routes/contact.js'));
app.use('/imprint', require('./routes/imprint.js'));
};
and in my app.js I require this file require('./server/router.js')(app);. When calling a route, for example /contact I require the contact.js
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.render('contact', { // render contact.hbs
title: 'Contact' // pass in some template variables
});
});
module.exports = router;
Within router.get I can use console.log(req.method) to log the HTTP method. Currently I would have to log this in all router files. Is it possible to have the code at one place like
module.exports = (app) => {
console.log(req.method); // Log the request method for all routes
app.use('/', require('./routes/home.js'));
app.use('/contact', require('./routes/contact.js'));
app.use('/imprint', require('./routes/imprint.js'));
};
Please provide a solution without installing middleware. I know there are many loggers out there.

If you want to avoid installing middleware and use console.log you can change your code as follows:
module.exports = (app) => {
app.use((req, res, next) => {
console.log('Request Type:', req.method);
console.log('Time:', Date.now());
next();
})
app.use('/', require('./routes/home.js'));
app.use('/contact', require('./routes/contact.js'));
app.use('/imprint', require('./routes/imprint.js'));
};
If you don't specify a route but have the next callback in, then it will execute your logging code first then the matching routing if there is one. You can find more details in the using middleware section of the ExpressJS API documentation.

Related

NodeJS Express middleware goes to next one without next()

I am trying to learn Express for NodeJS but I came across this:
I am trying to add 2 middlewares depeding on url, so on the /user to do something and on root to do something different. However the root middleware is always called even if i dont use next() and if i access the "/" url, the root middleware is called twice.
const express = require('express');
const app = express();
app.use('/user', (req, res, next) => {
console.log('In user middleware ');
res.send('<h1>Hello from User page</h1>');
});
app.use('/', (req, res, next) => {
console.log('In slash middleware !');
res.send('<h1>Hello from Express !</h1>');
});
app.disable('etag');
app.listen(3000);
it should be get or post not use
-get or post are routes
-use is middleware function
check this
const express = require('express');
const app = express();
app.get('/user', (req, res, next) => {
console.log('In user middleware ');
res.send('<h1>Hello from User page</h1>');
});
app.get('/', (req, res, next) => {
console.log('In slash middleware !');
res.send('<h1>Hello from Express !</h1>');
});
app.disable('etag');
app.listen(3000);
From an issue at GitHub.com
https://github.com/expressjs/express/issues/3260
Hi #davidgatti my "root path middlware" I assume you are talking about
nr_one. If so, yes, of course it is executed on every request; app.use
is a prefix-match system. Every URL starts with /, so anything mounted
at / will of course get executed :)
Okay, I can't confirm this but I suspect from the tutorial you are following you might be missing a line.
As you said, app.use is a middleware which will be added to all the route
So when you load say some url where you expect the middleware then it won't know about the request type (post, put, delete or get request).
Any alternate for this could be to try something like this
app.use('/user', (req, res, next) => {
if (req.method === 'GET') {
console.log('In user middleware ');
res.send('<h1>Hello from User page</h1>');
}
});
Again, Just check and compare his code thoroughly
Adding this link from Justin's answer for reference
In order to avoid such a problem, you have to use the return keyword.
When you send a file to the server, use return.
Try the following code,
const express = require('express');
const app = express();
app.use('/user', (req, res, next) => {
console.log('In user middleware ');
return res.send('<h1>Hello from User page</h1>');
});
app.use('/', (req, res, next) => {
console.log('In slash middleware !');
return res.send('<h1>Hello from Express !</h1>');
});
app.disable('etag');
app.listen(3000);
At line 13 and 8, I used the return keyword.
So when you make http://localhost:3000/ request, you will receive
Hello from Express !
And whenever you make http://localhost:3000/user request, you will receive
Hello from User page

Is it possible to have conditional routes in Expressjs?

What I want to do
if req is from mobile
first check these routes
router.use('/', mobileRoutes);
then ones below
router.use('/', routes);
but as far as I understand express goes through all use statements upon every request, it runs them all once and put route handlers into some sort of array. that get hit with requests.
For some urls I want to server different pages but no all (loginPage, registrationPage, etc).
Is it possible to do conditional routing, to prepend more routes in case some condition is met.
What I do have working is this:
router.use(function (req, res, next) {
let md = new MobileDetect(req.headers["user-agent"]);
if (md.mobile()) {
req.url = '/mobile'+req.url;
}
next();
});
//mobile routes
router.use("/mobile", require("./mobile"));
but it completely blocks all routes below.
I want a clean solution, One another option, which is not clean, is to add middleware to individual routes but that muddies code endlessly.
My Solution for conditional routing:
index.js
.....
//mobile routes
router.use("/", require("./mobile"));
router.get("/", require("./get"));
.....
mobile.js
const express = require("express");
const router = express.Router();
const MobileDetect = require("mobile-detect");
router.use(function (req, res, next) {
let md = new MobileDetect(req.headers["user-agent"]);
(md.mobile())? next() : next("router")
});
router.get("/", function (req, res) {
res.send({ query: req.query });
});
module.exports = router;

express router with id

I know I can do this in Express:
app.use('/:id', function (req, res){ // or app.get
console.log('Test param: ' + req.params.id); // "Test param: foo"
});
to get my url parameter.
When I try to use a router logic I did:
index.js
var sites = require('./routes/sites');
app.use('/:id', sites);
routes/sites.js
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
console.log(req.params.id); // logs "undefined"
// etc ...
});
module.exports = router;
this logic does not work, I get {} in the shell command, undefined for req.params.id.
Any idea where my logic is wrong?
It doesn't work, because that's just not how express interacts with its Routers. The reason you're not seeing id inside your router, is because :id was not declared within its scope. You'd need to do something like
app.use('/', sites);
and in your router
var express = require('express');
var router = express.Router();
router.get('/:id', function(req, res, next) {
console.log(req.params.id);
// etc ...
});
module.exports = router;
Additionally you can try playing around with app.param() see docs for examples http://expressjs.com/api.html#app.param
When you are trying to do this in index.js and routes.js -
app.use('/:id', function (req, res){ // or app.get
console.log('Test param: ' + req.params.id); // "Test param: foo"
});
router.get('/', function(req, res, next) {
console.log(req.params.id); // logs "undefined"
// etc ...
});
"id" is not defined there because that's how express works with routers, it just matches the routes matching with the specified prefix in app.use() method. For that, you can try using -
const express = require('express');
const router = express.Router({mergeParams: true});
Now it will work perfectly.
It actually works now.
You can do something like
app.use('/:id/transactions', require('./routes/transactions'));
In the main file, and then add {mergeParams:true} to Router options in the controller file.
const router = require('express').Router({mergeParams:true});
So here you will be able to get the id that was set in base Route.
// localhost:5000/:id/transactions/
router.get('/',(req, res) => {
console.log(req.params.id);
}
Found here:
http://expressjs.com/api.html#app.param
In your file router/sites.js, add an option to express router:
const router = express.Router({mergeParams:true});
Now you have access to id in sites.js file.

Chaining multiple pieces of middleware for specific route in ExpressJS

I want to just verify something but have't been able to find anything in the Express docs or online regarding this (although I know it's a feature).
I could just test this out but I don't really have a nice template and would like to hear from the community.
If I define a route in express like such:
app.get('/', function (req, res) {
res.send('GET request to homepage');
});
I can also define a middleware and load it directly, such as
middleware = function(req, res){
res.send('GET request to homepage');
});
app.get('/', middleware)
However, I can also chain at least one of these routes to run extra middleware, such as authentication, as such:
app.get('/', middleware, function (req, res) {
res.send('GET request to homepage');
});
Are these infinitely chainable? Could I stick 10 middleware functions on a given route if I wanted to? I want to see the parameters that app.get can accept but like mentioned I can't find it in the docs.
Consider following example:
const middleware = {
requireAuthentication: function(req, res, next) {
console.log('private route list!');
next();
},
logger: function(req, res, next) {
console.log('Original request hit : '+req.originalUrl);
next();
}
}
Now you can add multiple middleware using the following code:
app.get('/', [middleware.requireAuthentication, middleware.logger], function(req, res) {
res.send('Hello!');
});
So, from the above piece of code, you can see that requireAuthentication and logger are two different middlewares added.
It's not saying "infinitely", but it does say that you can add multiple middleware functions (called "callbacks" in the documentation) here:
router.METHOD(path, [callback, ...] callback)
...
You can provide multiple callbacks, and all are treated equally, and behave just like middleware, except that these callbacks may invoke next('route') to bypass the remaining route callback(s). You can use this mechanism to perform pre-conditions on a route then pass control to subsequent routes when there is no reason to proceed with the route matched.
As you can see, there's not distinction between a middleware function and the function that commonly handles the request (the one which is usually the last function added to the list).
Having 10 shouldn't be a problem (if you really need to).
Express version "express": "^4.17.1" or above
From the document: Series of Middleware
var r1 = express.Router();
r1.get('/', function (req, res, next) {
next();
});
var r2 = express.Router();
r2.get('/', function (req, res, next) {
next();
});
app.use(r1, r2);
Let's try a real life example:
tourController.js
exports.checkBody = (req, res, next)=>{ // middleware 1
if (!req.body.price){
return res.status(400).json({
status:'fail',
message:'Missing price!!!'
})
}
next();
}
exports.createTour = (req, res) => { // middleware 2
tours.push(req.body);
fs.writeFile(
`${__dirname}/dev-data/data/tours-simple.json`,
JSON.stringify(tours),
(err) => {
res.status(201).json({
status: 'success',
data: {
tour: newTour,
},
});
}
);
};
tourRouter.js
const express = require('express');
const tourController = require('./../controller/tourController')
const router = express.Router();
router.route('/')
.get(tourController.getAllTours)
.post(tourController.checkBody, tourController.createTour);
//muliple Middleware in post route
module.exports = router //need this or the following step will break
app.js
const express = require('express');
const tourRouter = require('./route/tourRouter');
const app = express();
app.use(express.json());
app.use('/api/v1/tours', tourRouter);
module.exports = app;

How can I obtain matched route pattern in express JS?

I want my logger middleware to log each matched route when response is sent. But there may be any number of nested subroutes. Let's suppose I have this:
var app = express();
var router = express.Router();
app.use(function myLogger(req, res, next)
{
res.send = function()
{
//Here I want to get matched route like this: '/router/smth/:id'
//How can I do this?
});
}
app.use('/router', router);
router.get('/smth/:id', function(req, res, next)
{
res.send(response);
});
Is it possible?
Because app-level middleware has no knowledge of routes, this is impossible. However, if you use your logger middleware as route middleware like:
router.get('/smith/:id', logger, function (req, res) { ... });
You can use a combination of two parameters on the request object:
req.route.path => '/smth/:id'
req.originalUrl => '/router/smth/123'
I'll leave it up to you how you want to combine both into one string.
Here's the code (in express 2.x)
// npm -v express
// 2.14.2
var app = express();
var router = express.Router();
app.use(function(req, res, next) {
var routes = app.routes; // See Reference 1.
for(var method in routes) {
if(routes.hasOwnProperty(method)) {
for(var route in routes[method]) {
if(req.url.toString().match(routes[method][route].regexp)) {
console.log('Route Debugger: ' + routes[method][route].path);
}
}
}
}
next();
});
app.use('/router', router);
router.get('/smth/:id', function(req, res, next)
{
res.send(response);
});
What does this do?
It queries the app.routes object. Now we have access to all the routes defined in our application.
We match the current url req.url with the regular expression of each route.
Since this is a application level middleware, it runs for every request. So you get logging like Route Debugger: /router/smth/:id, if you hit a url like /router/smith/123
Reference 1 : http://thejackalofjavascript.com/list-all-rest-endpoints/
Reference 2 : How to get all registered routes in Express?

Resources