Express app - Execute function on many but not all requests - node.js

What is the best way to call a function on many but not all requests in a node express app? (An example would be a function which checks if the user is currently logged in)
What I did is to define a module exporting a checkLogin(...) function and to call this function on each corresponding api-request. E.g.:
Module auth:
module.exports = {
checkLogin: function(req, res, next) {
if (req.session.hasOwnProperty('user')) {
//if the user is logged in we pass through
next();
} else if (req.cookies.user == undefined || req.cookies.pass == undefined) {
res.render('login', { title: 'Login' });
} else {
User.checkLogin(req.cookies.user, req.cookies.pass, true, function(o) {
if (o != null) {
req.session.user = o;
next();
} else {
res.render('login', { title: 'Login' });
return;
}
});
}
}
};
Routes for /index:
//...
var auth = require('../middlewares/auth.js');
//...
router.get('/index', auth.checkLogin, function(req, res) {
//if we passed the auth.checkLogin step we render the index page
res.render('index', {
title: 'Index',
udata: req.session.user
});
});
In another route file:
//...
var auth = require('../middlewares/auth.js');
//...
router.get('/user/someAPICall', auth.checkLogin, function(req, res) {
...
});
Is this the way to go or are there better ways to do that? I could define a middleware function which I could include using app.use(function(){..}) in each route. The problem is that every request for this route would go through this function which is not what I want.

Routers (http://expressjs.com/en/guide/routing.html) are a great way to design your application. You could think of your URL paths as namespaces, and create a router for the namespace that requires user authentication.
Most likely your main /index page won't require immediate redirecting to login, since it's used for presentation purposes; but if required, then just include the auth.checkLogin as you did above.
For everything else where you need your user to be authenticated (e.g. everything under /user/*), you'd better create a scoped router
const router = express.Router();
router.use(auth.checkLogin);
router.get('/someAPICall', fn1, fn2);
router.get('/someOtherAPICall', fn3, fn4);
and then in your parent router or main app, just include the router:
app.use('/user', router);
which is just like defining:
app.use('/user/someAPICall', [auth.checkLogin, fn1, fn2]);
app.use('/user/someOtherAPICall', [auth.checkLogin, fn3, fn3]);
This gives you the advantage of creating modular route handlers - which makes them easier to adjust, reuse, etc. - and at the same time will keep auth.checkLogin, although always executed when the router is entered, just for the paths defined by the router.
In short, the approach would be: "execute function on all routes inside the router, but not on all the app requests".
If you cannot redesign your routes in this way, then yes, you'll always need to include auth.checkLogin in handlers list for the paths you only want to use.

Related

How do I share functionality between my expressjs routes?

Suppose I have two routes in my expresjss project: users.js and profiles.js.
users.js:
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
var extra = something_nice();
res.json( { three: 'four', extra: extra } );
});
module.exports = router;
profiles.js:
var express = require('express');
var router = express.Router();
/* GET profiles listing. */
router.get('/', function(req, res, next) {
var extra = something_nice();
res.json( { one: 'two', extra: extra } );
});
module.exports = router;
Notice how I have something_nice() method there, which ideally I would define in a 'super class' if this were regular OOP like rails controllers.
How do I go about this with node + expressjs? My assumption was I should create a new module, and require it here, but is this the best practice?
You solve it in the same way. Using a class:
class SomethingCool {
somethingNice() {
return 'cool!';
}
}
module.exports = SomethingCool;
In your route:
var express = require('express');
var router = express.Router();
var SomethingCool = require('./something-cool.class');
const something = new SomethingCool();
/* GET profiles listing. */
router.get('/', function(req, res, next) {
var extra = something.somethingNice();
res.json( { one: 'two', extra: extra } );
});
module.exports = router;
Same principles should apply regardless of language (as long as they have support for classes, or class-like objects).
Also you don't even need a class here:
function somethingNice() {
// some logic
}
// inside file1
router.get('/', (req, res, next) => {
const extra = somethingNice();
res.json({ one: 'two', extra });
});
You can reuse functions where you like/need, just ensure they're exported using module.exports (if using in a different file/module).
Have a read over this when you have time:
https://dev.to/santypk4/bulletproof-node-js-project-architecture-4epf
It may be able to answer some of the questions you later have about design, structure and reusing logic in different areas.
EDIT: an explanation on how middleware can help in certain situations.
https://expressjs.com/en/guide/using-middleware.html
function isUserAdmin(user) {
// some logic for determining
}
app.use((req, res, next) => {
if (isUserAdmin(req.user)) {
req.role = 'Admin';
}
next();
});
This is a simplified example, in reality you'd also need to add the req.user.
The thing to understand here, is your ability to use middleware to reuse functionality.
However, if you wanted something a little more specific to the route, then I'd opt to use a class (following the Service pattern from other reference link).
Another (more common) example, consider a logger, it outputs which endpoint was requested and with what method:
// file: service/logger.service.js
class LoggerService {
log(message) {
console.log(message);
}
}
module.exports = MyLogger;
// file: middleware/logger.middleware.js
const logger = new MyLogger();
app.use((req, res, next) => {
const path = req.path;
const method = req.method;
logger.log(`${path} ${method}`);
return next();
});
This way, your route never needs to know about the logger, or what function it has, you can plug an unlimited amount of additional functionality this way.
Although it's more suited for generic tasks, like checking a user has authenticated (for example), or is authorized, but it's certainly not limited to only that type of use.
If you really want a super class, then use a singleton:
class SuperClass {
constructor() {
this.someOtherClass = new UserClass();
this.someSecondClass = new ProjectClass();
}
doSuperWork() {
}
}
module.exports = new SuperClass();
I'll say though, it may not be the best solution (super classes in general).
You can create a function that accepts the router instance as an argument and can implement your common logic inside that function. Javascript uses composition instead of inheritance.
module.exports=function (router){
router.get('/', function(req, res, next) {
var extra = something_nice();
res.json( { one: 'two', extra: extra } );
});
}

How to routing for mobile view in express

I want to change the URL for mobile. www.example.com to m.example.com
I use isMobile function to detect device type.
I do not know what to do for routing.
for example: m.example.com/index
please help me
in app.js
function isMobile(req, res, next) {
if ((/Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Kindle|Silk|Opera Mini/i.test(req.headers["user-agent"]))) {
res.redirect('//m.' + req.host+ req.path);
} else {
next()
}
}
app.get('/',isMobile,routes.index);
If device is a mobile,i want to run routes.mobileindex. How?
If you want to render a separate view for mobile devices in the same application then it would be pretty straigh forward. it is a just a matter of creating another view file and you can apply your separate layout and CSS style to design it.
var express = require("express");
var router = express.Router();
function isMobile(req, res, next) {
if (
/Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Kindle|Silk|Opera Mini/i.test(
req.headers["user-agent"]
)
) {
// Instead of redirecting to another view you can also render a separate
// view for mobile view e.g. res.render('mobileview');
res.redirect("/mobile/device");
} else {
next();
}
}
/* GET home page. */
router.get("/", function(req, res, next) {
res.render("index", {
title: "Express"
});
});
router.get("/device", isMobile, function(req, res, next) {
res.json("normal view is rendered");
});
router.get("/mobile/device", function(req, res, next) {
res.render("mobile");
});
module.exports = router;
I have set a redirection here but this is not the best way if you are in the same application, because you can directly hit /mobile/device view, in that case you also need a middleware or can reuse the isMobile middleware to redirect to normal view. I would rather suggest to use res.render('yourviewname')
Hope this gives you an idea to proceed futher!

express response locals disappear

I would like to send error messages back to the client without adding them to the url. Here is my attempt:
exports.register = function(req, res) {
if (req.body.password != req.body.password_repeat) {
res.locals.err = 'Passwords must match.';
res.locals.action = 'register';
res.redirect('/');
return;
}
...
exports.index = function(req, res) {
req.url = '/';
res.render('index', {
action: res.locals.action,
error: res.locals.error,
redirect: res.locals.redirect
});
};
So the redirect works fine and exports.index executes. The problem is that res.locals are gone by then. Is this because once I redirect it is considered a new req/res cycle? Any way I can pass this information through redirect without doing something like res.redirect('/?error=error')
You can use flash package from expressjs, but you need to have session middleware to use it. Also, you can use express-flash package from RGBboy but you need to have both cookieParser and session middlewares in this case.

Using Express 4 how to redirect to my own route without losing req and response data?

I have my application structured with 3 Routes (api, admin, default). Each lives in there own file and has it's own middleware and exports a Route. The problem I am facing is when I want to forward to another route that lives on a different router. Essentially I want to call the same function so that I am not serving up the same view from multiple locations.
I don't want to user res.redirect('/someplace') because I want to be able to pass the req and res objects on to the method.
|-app.js
|-routes
|---admin.js
|---api.js
|---default.js
The routes are required and used in app.js as follows
app.use('/api', require('./routes/api')(passport);
app.use('/admin', require('./routes/admin')(passport);
app.use('/', require('./routes/default')(passport);
Inside of admin if have a situation where I need redirect to login and pass some data
// authenticates all routes for the admin router
router.use(function(req, res, next){
if(req.isAuthenticated()){
return next();
}
res.flashMessage.push('Session expired'); //is lost after redirect
res.redirect('/login');
//do I need to restructure my whole app so that I don't
//have to call res.redirect('login')
});
Any ideas on how to structure this? Do I need to export every method and keep all of my routes in one router file? That doesn't very clean, but if the functions are somewhere else it may be too messy.
You can forward it by calling the next callback ,but only if you do not use any paths.
app.use(function(req, res, next) {
// ... api
next();
});
app.use(function(req, res, next) {
// ... admin
next();
});
Another option is use * that will match all paths:
app.use("*", function(req, res, next) {
var path = req.path; // just example how it can be done
if (path === "/api") {
// ...
path = "/admin";
}
if (path === "/admin") {
// ...
}
});
Edit:
I don't think that express has something like next('/login'); ,so basically function that can forward a request to another path and I don't think that is right to have something like this. If a client ask for /admin you should send this particular page and not the page that is under /login. If you want to send back to a client the login page than just redirect it as you did it in your question. I understand that you want to keep the req, res ,but then is the problem in the proposal/structure of your webapp.

Nodejs check for authentification before each routes rendering

I'm using sessions and cookies to authenticate the users. I would like to check for users having a cookie and if so i will set the sessions variables.
So basicly what i do is :
Check if sessions variables exist
If not, check if user has cookie
If he has a cookie, I compare the value in my database.
If everything's ok, I set up the session.
Now i'd like to have that process into a module so i don't have to paste that code into each routes of my site.
Let's say I've put all that code in a middleware route located at routes/middleware/check_auth.js.
How do I export this module so I can check in my route page if the user has auth or not, something like :
//routes/index.js
var check_auth = require('./middleware/check_auth');
module.exports = function(app){
app.get('/', check_auth, function(req, res){
if(variable_from_check_auth == true){
res.render('index_with_auth');
}else{
res.render('index_without_auth');
}
});
};
Btw, I'm not sure if it's the right way to do or if I simply have to :
Call the module on each routes.
Check for some sessions variables before rendering.
If someone could help me!
You can just export your middleware as simple as this(assuming you are using express session handler and cookie parser):
var userModel = require('./user');
module.exports = function check_auth(res, req, next) {
if (!res.session) {
req.send(401);
return;
}
userModel.isAuthenticated(req.session.id, function (result) {
if (!result) {
req.send(401);
return;
});
next();
});
};

Resources