How to access application-level middleware from router? - javascript

I am trying to access my application-level middleware from router in a project generated with express application generator.
Middleware is used to query database with user ID received from router.
I feel like I'm missing something very simple (or fundamental) but can't get around the problem (this being my first Node.js project). So more than best practice I'm looking for a simple solution
I've tried using different app methods including post.
/app.js
var MyAppMidW = function (req, res, next) {
res.send(queryDB(req));
next()
}
app.use(MyAppMidW);
/routes/index.js
router.get("/dbquery", (req, res) => {
if (req.header('auth-header')) {
res.send(req.app.get.MyAppMidW(req.header('auth-header'))); //The problem
}
else {
res.send(req.app.get('defaultData')); //This works
}
});
Error messages include "$middleware is not a function" and "$middleware is not defined".
Solution
/app.js
app.MyAppMidW = function (req) {
queryDB(req);
}
/routes/index.js
router.get("/dbquery", (req, res) => {
if (req.header('auth-header')) {
req.app.MyAppMidW(req.header('auth-header'))); //Makes a database query
res.send(req.app.get('defaultData')); //Fetches database query result
}
else {
res.send(req.app.get('defaultData'));
}
});

If you do it like this
app.use(MyAppMidW);
Every request will query your db, and thats not what you want. I guess you use the MVC design pattern.
In your route folder you have something like this:
import appController from "../controllers/app.js"
router.get("/dbquery", appController.MyAppQuery)
And in your controllers folder you have your logic that querys the db
exports.MyAppQuery = (req, res){
//If u use mongodb for example
YourModel.find().then(data => {
res.json(data)
})
}

You need to call app.set("MyAppMidW", MyAppMidW) and then you can use get. Or do this inside the app.js file
app.MyAppMidW = function (req, res, next) {
res.send(queryDB(req));
next()
}
Then call it by req.app.get('MyAppMidW')(req.header('auth-header')) or req.app.MyAppMidW(req.header('auth-header')) inside the routes file.
But middleware is called automatically when you say app.use(MyAppMidW) the function is called by default on each request. So no need to call it explicitly inside the router function.

Related

Why does Swagger endpoint require headers?

So I have added some swagger configuration in a routes folder like so:
import express from 'express';
import swaggerUi from 'swagger-ui-express';
import swaggerDocument from '../swagger/swagger.json';
const router = express.Router();
router.get('/api-docs', swaggerUi.setup(swaggerDocument));
export { router as swaggerRouter }
Now there is an authentication process in the root app.ts file, but my understanding is if I add the swagger endpoint before it executes the authenticate logic, it should not ask for headers:
app.get('/', (req, res) => {
res.send('Howdy!');
});
app.use(swaggerRouter);
app.use(async (req, res, next) => {
const result = await auth.verifyAuth(req).catch((err) => {
return err;
});
if (result.httpCode == 200) {
res.locals.authResult = result
next()
} else {
res.send(result)
}
});
So the authentication logic from where verifyAuth comes from would make for a good middleware to target endpoints instead of the whole entire application, but to refactor it to work as such a middleware is a pain because the author wrote every function to depend on every other function.
And yet, if I add a random endpoint above that authenticate logic like:
router.get('/pingMe', (req, res) => {}
You can go to that one without being asked to provide headers.
What am I missing?
I literally removed the authentication logic and I am still unable to get to the swagger endpoint without being asked for headers.

Can I pass the parameters (req, res, next) of a function to another function?

I searched a little bit but I did not find what I am searching for.
I have Node application and two functions:
router.get('/get/user-relevant-data', (req,res,next)=>{
//some code is executed here
return res.status(200).json(userRelevantData)
})
router.get('/get/updated-user', (req,res,next) => {
// I want to call '/get/user-relevant-data' and assign the returned object to another variable
let userRelevantData = // how to call the function here correctly?
})
How would I do such things (if it's feasible) or should such code be avoided? If such code should be avoided, what else could I do except putting the code of the one function into the other.
you can change the way you set up router in that way you can apply as many middlewares as you want like this:
const middleware1 = require("....") //adress to the file your middleware is located
const middleware2 = require("....") //adress to the file your middleware is located
router.get('/directory', middleware1, middleware2 )
and in another file you define middlewares in this way :
exports.middleware1 = (req, res, next) => {
//do some coding
req.something= someDataToPass
next()
//you add the data you want to pass to next middleware to the req obj
// and then access that in the next middleware from the req object then
// call next to run the next middleware
}
then in another file or the same file you type another middleware like this:
exports.middleware2 = (req, res, next) => {
//do some coding
data = req.something
//get data from last middeleware
res.json({})
}
and at the same time you have access to all the req data in both middlewares

Pass data between express router and middleware

I'm trying to write express middleware to check the validity of a JWT in the Authorization header. This seems quite easy but I don't want it to run on all routes (e.g. not on login/signup routers).
So, I'd like to specify in the router declaration that a route should require a valid token. E.g. something like this
const controllers = require('../controllers');
module.exports = (app) => {
app.post('/auth/signup', controllers.auth.signup.post);
app.post('/auth/login', controllers.auth.login.post);
app.get('/teams', controllers.teams.get, {requiresToken:true});
};
Except, .post and .get don't take a third parameter and the controller only takes (req,res,next) parameters so I can't really see a way of passing startic data for each route. I'm sure I'm missing something simple
This is how i created a middleware to pass the data into
module.exports = function(options) {
return function (req, res, next) {
//write your code here
// here you can access options variable
console.log(options.data)
next();
}
}
How you call that middleware is like this
app.use(middleware({'data' : 'Test'}));
To use on route basis
app.post('/userRegistration', middleware({'data' : 'Test'}), (req, res) => {});
You can exclude the auth subroute from this middleware using negative lookup regexp:
const controllers = require('../controllers');
module.exports = (app) => {
app.use(/\/((?!auth).)*/, yourJwtTokenValidatorMethod); // replace with your jwt token validator middleware
app.post('/auth/signup', controllers.auth.signup.post);
app.post('/auth/login', controllers.auth.login.post);
app.get('/teams', controllers.teams.get, {requiresToken:true});
};

Proper way of setting up access rights?

I am creating a user management system - However I am current finding myself checking the user type on a per router bases.
router.get('/admin/settings', (req, res) => {
if(admin) {
//Proceed.
}
}
router.get('/admin/users', (req, res) => {
if(admin) {
//Proceed.
}
}
Is there a better way of doing this? Can't I just set a route like this?
router.get('/admin*', (req, res) => {
if(!admin) {
res.status(404).send('Not found')
}
}
(I have tried and not succeeded, feels like it clashes with other routes)
Also, on a similar note. How Am I supposed to handle denying a user access to a script? Do I send a 404 or 403?
You can use an Express middleware function:
router.use(function(req, res, next) {
if(admin) {
return next();
}
// We fail closed
return res.status(403).send('Forbidden');
});
// This won't get called if the middleware doesn't call next()
router.get('/admin/settings', (req, res) => {
// Do stuff
}
Here, we call next() only if the user is an admin, which allows the call to continue. Any routes added after this middleware will be protected.
Also, on a similar note. How Am I supposed to handle denying a user access to a script?
A 403 is the appropriate code here, though a 404 can also be used if you wish to hide the route from unauthorized clients. I would suggest reading up on what each code is designed for.

Express.js require session to access particular routes

I have an app where certain pages require that the user be logged in.
I am not sure if there is something built in for this, but what I have for doing this is as follows:
app.use((req, res, next) => {
if (req.session.username) {
app.get('/project/create', projectCtrl.create)
app.get('/project/create/save', projectCtrl.save)
} else {
return res.redirect('/')
}
next()
})
Is this the correct way of doing this, or is there a better way in express? The way I am doing it kind of feels a little hacky.
Yes, that's one correct way of doing it. What you have is an application-level middleware in express. It gets called for every request the application receives.
You can extract the username check and apply that as a route middleware substack. This way the middleware only gets executed for the routes it's applied to.
function gatePass(req, res, next) {
if(req.session.username) {
next();
}
else {
return res.redirect('/');
}
}
app.get('/project/create', gatePass, projectCtrl.create)
app.get('/project/create/save', gatePass, projectCtrl.save)
You can take this a bit further if you'll like to separate concerns by using express router together with route-level middleware. This also applies a middleware directly to the routes.
var router = express.Router();
router.use('/project/create', gatePass);
router.use('/project/create/save', gatePass);
router.get('/project/create', projectCtrl.create);
router.get('/project/create/save', projectCtrl.save);
app.use('/', router);
this solution work. It's not the best but for small project it will be good. The only drawback is that you will need to define every route you want to be check with a session.
Nodejs is the world of middleware, so why not use one? I think it's the best thing to do.
Verify is a file where I export my middleware and I apply it on all my router.. (in this case it's just to check if the user is logged or not)
var verify = require('./verify');
router.all('/*', verify.isLogged, function(req, res, next) {
if(req.decoded._doc.isLogged == "") {
next();
}
else {
res.json("error");
}
});
This way, if in the future you need to check one thing, then another one, you will just need to call you function where you want to check
router.get('/test', verify.isLogged, verify.isAdmin function(req, res, next) {
if(req.decoded._doc.isAdmin == "") {
next();
}
else {
res.json("error");
}
});

Categories

Resources