Node API: Paramterization in routing - javascript

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.

Related

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.

Express routing - prevent app.use('/') top-level path middleware from executing when visiting child/nested paths directly

So I'm not really sure if the title is descriptive enough, but here is a super simple example.
My site has a public area and a restricted admin area.
example.com/admin (admin home page)
example.com/admin/news (news page)
example.com/admin/posts (posts page)
And because I don't want people who aren't administrators or logged in to be able to access it, I have a simple middleware function to check for cookies.
app.js
const express = require('express');
const app = express();
const authMiddleWere = async (req, res, next) => {
// pseudo-code, do some cookie validity check here
console.log(`Route: ${req.url}`)
if (cookie) {
next();
}
};
const adminRouter = require('./routes/private/home');
const newsRouter = require('./routes/private/news');
const postsRouter = require('./routes/private/posts');
app.use('/admin/', authMiddleWere, adminRouter);
app.use('/admin/news', authMiddleWere, newsRouter);
app.use('/admin/posts', authMiddleWere, postsRouter);
/routes/private/home.js
const express = require('express');
const router = express.Router();
router.get('/', async (req, res, err) => {
res.render('private/home');
});
module.exports = router;
The problem here is that this authMiddleWere function gets called twice when I visit nested paths such as example.com/admin/news which shares the same pattern - it's starting with /admin/......
I can tell that for sure because we are logging the req.url in our middleware function so if I go to example.com/admin it will log out:
Route: /
But if I go to example.com/admin/news it will log out both:
Route: /
Route: /news
So what is causing this and how do I work my way around it? I'm assuming that what I described is the intended behavior of Express.js so I am looking for a way to get around this or (re)structure my code better.
Cheers!
You can use a regex for your route.
app.use(/\/admin$/, authMiddlewear, authRouter);
This will match only routes that end in admin. You may need to handle cases where the route is /admin/ instead of /admin, but iirc, express handles that intelligently.
Well one way you can fix this is by creating a separate route file and splitting everything into a MVC manner. For example:
Inside your main app.js just create a route pointing to the /admin like so:
app.use('/admin', authMiddleWere, require('./src/your-route-to-the-file/admin.route'));
Inside the admin.route file, call your controller like this:
const express = require("express");
const router = express.Router();
const mainAdminCtrl = require("../controllers/admin.controller");
router.get("/news", mainAdminCtrl.adminAuthDisplay);
module.exports = router;
Where the const mainAdminCtrl is your controller and the function adminAuthDisplay is your service.
Essentially, you are splitting your functionality in to a dedicated router, controller and service file. So when you try to access the route /admin, it will look for any suffix inside the router file.
In a case where you want to access the /news endpoint, your API will only make the call once.
If this helps, I can expand my explanation further.

Specifying a specific route in an Express Router used with an array of routes

The Express docs are clear about the fact that you can supply app.use with an array of routes for a particular piece of middleware.
They also describe how to split routers into separate files here.
But I can't see how to combine these two approaches.
index.js
const authRoutes = require('./routes/auth');
app.use(['/api/register', '/api/login'], authRoutes);
./routes/auth
router.route('/')
.all((req, res) => res.send('responds to both routes'); // <- this works
router.route('/register') // <- But how can I specify a specific route?
.all((req, res) => {
res.send('The register route');
});
It seems like you are trying to do something like this
const authRoutes = require('./routes/auth');
app.use('/api', authRoutes);
router.route(['/register', '/login']) . // respond to both
.all((req, res) => res.send('responds to both routes');
router.route('/register') // this is /api/register
.all((req, res) => {
res.send('The register route');
});
router.route('/login') // this is /api/login
.all((req, res) => {
res.send('The login route');
});
However there is something wrong with this logic , because your single routes /register and /login in auth file will never be called when you pass /api/login or /api/register they will go through the first array route. If you put the first route to the bottom then the array route respond to both route will be never called since it either goes to first /register route or 2nd /login route.

NodeJS: How to add routes afterwards (after middleware)

i'm currently working on a project with node.js.
I stucked at a specific problem. After adding all routes
with express (app.get("..", func)) I end up with a middleware that
catches all requests and redirects to a 404-page.
The thing is now, when I add a route afterwards during server run, the middleware
doesent care about the new route.
example:
app.get("/home", function(_, res) {
res.send("home");
})
app.get("/faq", function(_, res) {
res.send("faq");
})
app.use(function(_, res) {
res.send("404");
});
// e.g. 10 min later..
app.get("/team", function(_, res) {
res.send("team");
})
So i can access /home and /faq but after 10 min requesting the page /team, i am redirected to the 404 page.
Does anybody know a solution ? Im quite new to nodejs..
Although adding routes dynamically doesn't sound like a good idea, here's a workaround: add an (empty) Router instance right before the 404 handler, and add the new routes to that router instead of to app:
let router = express.Router();
app.use(router);
app.use(function(_, res) {
res.send("404");
});
// e.g. 10 min later..
router.get("/team", function(_, res) {
res.send("team");
})

How to avoid the ambiguity of routes in Express.js

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

Categories

Resources