How to avoid the ambiguity of routes in Express.js - javascript

I have three files server.js, views.js and access.js
In server.jsI have put all the dependencies and some routes like
app.post('/services/getallversions', (req, res) => {
...
// code to handle the req and send response
})
In views.js I have code like below,
module.exports = function(app, db, bodyParser, rules, constants, query) {
app.post('/services/user/:user/:type', (req, res) => {
// user can be 'abcd'
// type can be addview, deleteview etc.
...
// do processing for the req and send res
})
}
In access.js I have code like,
module.exports = function(app, db, bodyParser, rules, constants, query) {
app.post('/services/user/:user/:type', (req, res) => {
// user can be 'abcd'
// type can be addaccess, removeaccess etc.
...
// do processing for the req and send res
})
}
In server.js file I require the access.js and views.js in following way,
var access = require('./access')(app, db, bodyParser, rules, constants, query)
var views = require('./views')(app, db, bodyParser, rules, constants, query)
When I try to POST using /services/user/abcd/addaccess my views.js file code gets executed. constants, query, rules are other .js file which is already used in server.js using require('./filename').
I understand that the ambiguity causes due to same URL structure. I am using Express 4 and Node JS 6. I want to separate code of access.js and views.js from server.js and put them in separate files and require them in the above mentioned manner. views.js and access.js are created by me. They are not any Javascript Framework or something like that.
In view.js I have also tried the following code
var router = require('express').Router()
router.post('/services/user/:user/:type', (req,res)=>{})
But the same problem exists. Is there any way to achieve the thing ?

I suggest you use "miniApp" concept in Express, where each "miniApp" is distinguished using name-space.
For example:
Main App:
All routes with '/views/...' prefix will go to viewsCtrl. This middleware should appear before your default/main app routes:
var viewsCtrl = require('./views');
app.use('/views', viewsCtrl);
Inside views.js:
var router = require('express').Router();
// complete route /views/services/user/:user/:type
router.get('/services/user/:user/:type', function(req, res){...});
module.exports = router;
Same for access.js.

The routes are identical and express will never be able to tell which one to call. Order is not the problem here; as Chris G said in his comment, the second call to app.post(...) will overwrite the first (think of URLs as keys in a hashset).
You already know that the url will be in the format of /addview or /removaccess etc, so you can put that knowledge in the routing middleware:
// access.js
app.post('/services/user/:user/access/:type', (req, res) => {
// ... type is now only add, remove, etc...
})
// view.js
app.post('/services/user/:user/view/:type', (req, res) => {
// ...
})
or even (I think):
// access.js
app.post('/services/user/:user/:type((access)$)/', (req, res) => {
// ... will match addaccess, removeaccess and so on
// but I'm not entirely sure ...
})
Reference here:
https://expressjs.com/en/guide/routing.html

Related

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

Startegy for organzing the endpoints in SPA app

I need strategy on organizing the urls.
Our application has around 150 urls. Some are rather simple without any Route parameters. While others have often more than 1 route parameter in them. For example it could be like this
api/School/Class
api/School/1/Class/2/Student
api/School/Class/revaluate
So in the first one it has no parameter , while second has two parameters and finally third one has 1 but last part is not a resource but an action.
I don't want to store the url just where we would consume it, since maintaining the urls would be technical nightmare. I was hoping if we could have single file api.js or multiple files like api/School.js , api/Teacher.js for storing the files.
In express, you call this a router.
const express = require('express');
const app = express();
const schoolRouter = require('./routers/school');
//...
app.use('/api/school', schoolRouter); // Forwards any requests to the schoolrouter
//...
School.js:
// The router works just like express app (which is also a router)
const schools = require('express').Router();
// we used example.com/api/schools/something to get here
// but the router only cares about what comes next
schools.get('/', function(req, res, next) {
// res.send()
});
// Get a single school (etc, you get the point)
schools.get('/:schoolId', function(req, res, next) {
let id = req.params.schoolId;
// get data from server and res.send()
});
//...
module.exports = schools;
And you can chain routers, but because you only have a partial route, parameters might get lost. So the normal thing is to store parameters to the req object.
schools.use('/:schoolId/classes', function(req, res, next) {
req.schoolId = req.params.schoolId;
next()
}, classesRouter);
This way we can access req.schoolId at any time further down the chain.

How to access application-level middleware from router?

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.

Node API: Paramterization in routing

I'm trying to build up an api for my website. However I kind of struggle with the route params.
I have two routes for now:
const route1 = require('./routes/route1')
const route2 = require('./routes/route2')
app.use('/route', route1)
app.use('/route/:id/details', route2);
The first route works perfectly fine. Within the first route, I check for the ID param as well as I do gets like
Code in route1
router.get('/:id', async (req, res) => {
...
})
I can easily check for the ID as a paramter in my query.
I wanted to make a 2nd path, so that I can call for the details of one object.
However its not working as expected.
Code in route2:
router.get('/', async (req, res) => {
console.log(req.params);
})
My parameters are always empty in this path. (I already checked that it can get called)
Wherever you use router add this to the top of that file:
i.e. in router.js
const router = require('express').Router({mergeParams: true});
notice the {mergeParams: true} you need this since you're nesting your routes in another file. If you left your routes all in on file, i.e.
app.use('/route/:id/details', (req, res) => {
console.log(req.params);// works fine
});
then you wouldn't need the mergeParams true flag.

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});
};

Categories

Resources