ExpressJS Router is not handling root routes - node.js

UPDATE 2: I just noticed that if on the app file I change the line:
app.use('/', home);
to:
app.use('/anything', home);
then all of the sub routes "roots" work fine.
UPDATE 3: I just realized something else. A method on the home.js file that I did not originally include below since I didn't think was relevant turns out to be the cause of the problem.
router.get('/:adventureId', (req, res) => {
var data;
//Irrelevant content that sets data as a JSON object.
res.json(data);
});
Turns out that every single sub route "root" is passing through here and since on the other routes adventureId is undefined then data is just an empty JSON object.
So the real question is: If this router bound to "/" and the other "roots" are bound to "/adventure" and "/test" why are all of them going through "/:adventrueId"?
I have a very simple ExpressJS application on which all of the "roots" in each and every single route other than home are not being handled and they always display an empty JSON object on the page.
In some posts it was mentioned that it could be a caching issue since these routes always return a 304 status, but I've tried doing an "Empty cach and hard reload" on Chrome and even with the 200 status I still get a blank page with an empty JSON object displayed on it. I tried it with MS Edge and got the exact same behavior.
Here's what I have:
On my app file
var app = express();
var home = require('./routes/home');
var adventure = require('./routes/adventure');
var test = require('./routes/test');
app.use('/', home);
app.use('/adventure', adventure);
app.use('/test', test);
On home.js file:
var express = require('express');
var router = express.Router();
router.get('/', (req, res) => {
console.log("This works fine with http://localhost:3000.");
res.render('home');
});
router.get('/:adventureId', (req, res) => {
var data;
//Irrelevant content that sets data as a JSON object.
res.json(data);
});
module.exports = router;
On the adventure.js file:
var express = require('express');
var router = express.Router();
router.use('/:id', (req, res) => {
console.log("This works fine with http://localhost:3000/adventure/5.");
next();
});
router.get('/:id', (req, res) => {
console.log("This works fine with http://localhost:3000/adventure/5.");
res.render('adventure');
});
//I've also tried putting this before the other routes and the result is the same.
router.get('/', (req, res) => {
console.log("This is never written in the console with http://localhost:3000/adventure.");
res.send("This is never rendered in the page.");
});
On the test.js file:
var express = require('express');
var router = express.Router();
router.use('/', (req, res) => {
console.log("This is never written on the console with http://localhost:3000/test.");
res.send("Hello from the test root route");
});
module.exports = router;
In the ExpressJS Router documentation and every single blog and example I've found it says that this is how it should work so I am really at a loss here.
Thank you.

If this router bound to "/" and the other "roots" are bound to "/adventure" and "/test" why are all of them going through "/:adventrueId"?
Because Express doesn't match routes based on which one matches best, it matches on which one matches first.
In your case, the route /:advertureId was declared before the routes for /adventure or /test. And /adventure and /test both match /:advertureId, so the handler for that route is called.
If you want to prevent this, declare more specific routes first:
app.use('/adventure', adventure);
app.use('/test', test);
app.use('/', home);

After much going around, several updates on the question and banging my head against the keyboard I finally understood what the problem is:
By having the routers set up like this:
app.use('/', home);
app.use('/adventure', adventure);
app.use('/game', gameService);
app.use('/test', test);
and on the "home" router having this method signature:
router.get('/:adventureId', (req, res) => {
var data;
//Irrelevant content that sets data as a JSON object.
res.json(data);
});
Every single sub route "root" was being interpreted as the parameter of the previous method so:
/adventure
/test
/anything
would be handled as:
adventrueId = adventrue
adventureId = test
adventrueId = anything
The solution was to do this:
app.use('/adventure', adventure);
app.use('/game', gameService);
app.use('/test', test);
app.use('/', home);
And now everything works.

Related

How do I get the full route path including the parameters from express or extend the request object to do so?

I have the following route in my express (version 4.17.1) API in a postTimecardCompany.js file:
const mongoose = require('mongoose');
const Timecard = require('./../models/timecard');
function postTimecardCompany(server) {
server.post('/api/v1/timecard/:userId', (req, res) => {
// Insert timecard data into the database
Timecard.create(data, (error, result) => {
// Check for errors
if (error) {
res.status(500).end();
return;
}
// Respond
res.status(200).send({timecardId: result._id});
});
});
}
module.exports = postTimecardCompany;
The route (among other routes) is loaded via the following mechanism by server.js file:
[
'postTimecardCompany',
'anotherRoute',
'someOtherRoute',
'andSoOn...'
].map((route) => {
require('./core/routes/' + route + '.js').call(null, server)
});
I have a middleware (in server.js file) where I check which route is being called.
server.use((req, res, next) => {
// If route is "/api/v1/timecard/:userId" do something
});
I have found various solutions which do nearly what I am looking for, but not exactly.
For example, if I post to the route with a data parameter userId value of "123f9b" then req.originalUrl gives an output of "/api/v1/timecard/123f9b."
What i'm looking to get is the original route path with the parameters in it so for a request of "/api/v1/timecard/123f9b" it would be: "/api/v1/timecard/:userId."
How do I get this functionality in express or extend express to get the original route path with parameters in the request object?
if you want to use from your approach, it's is impossible, after that your approach is not standard in express check the documentation, if you want get routes in a middleware you should try like this:
server.js
const express = require('express')
const server = express()
const postTimecardCompany = require('./routes/postTimecardCompany.js')// don't use map()
server.use("/",postTimecardCompany)//use the routes
server.listen(6565,()=>console.log(`Listening to PORT 6565`))
routes of postTimecardCompany.js
use Router of express and export router, and you can use middleware before each route you want, there are many ways to use middleware in routes, check the documentation
const express = require("express");
const router = express.Router();
const middleware = require('../middleware');//require middlewares
router.post("/api/v1/timecard/:userId", middleware,(req, res) => {
// Insert timecard data into the database
console.log(req.route.path);
});
module.exports = router;
middleware.js
module.exports = ((req, res, next) => {
console.log(req.route.path);
next()
});

How to split a routes.js that grows too large to be easily maintained?

I'm using node and express to create a rest api. I followed a tutorial where all the routes and its logic are saved in a routes.js file like this:
SERVER JS:
var express = require('express');
var app = express();
(...)
require('./app/routes.js')(app, port, express);
ROUTES.JS
module.exports = function(app, port, express) {
var apiRoutes = express.Router();
(...)
//Sample route
apiRoutes.get('/userfiles', function(req, res) {
UserFile.find({ owner: req.decoded.user.email }, function(err, filesList) {
if (err)
return done(err);
res.json({ success: true, files: filesList });
});
});
My problem is twofold:
1 - Routes can easily contain code thats 150 lines long, some of them far longer. It doesn't feel clean to have route declarations and the logic grouped together. Is it a good practice to do something like this instead?
apiRoutes.post('/randomRoute', function(req, res) {
return res.json(functionThatContainsTheActualCode(req));
});
(and then have an functionThatContainsTheActualCode function with all the logic in a different file).
2 - I have middleware that applies to some functions (for example, some routes are only accessible for logged in users and those routes go through an authentication middleware). Currently way I do it is declaring public routes before the middleware declaration and private routes after, which feels incredibly hacky. How can I separate public and private routes (and the middleware itself) in different files?
Problem 1:
We need to go deeper.
Change the route file to just require the actual router logic.
routes.js
// where app = express();
module.exports = (app) => {
// index.js happens to be a file exporting the router.
app.use('/', require('./index'));
// this is basically the idea. Create a separate file for the actual logic.
app.use('/route', require('.path/to/file'));
};
and in file.js
const express = require('express'),
router = express.Router();
router.verb('/path/', (req, res, next) => {
// do whatever
});
// this is required
module.exports = router;
Problem 2:
Middleware is basically a function taking in request, response, next as 3 params, doing something with the request and either sending out a response or moving on to the next middleware. That's why you need to call next if you want to move to next middleware in the chain.
Now all you need is a file that exports a function which takes request, response, next as params.
// lets call this auth.js
module.exports = function(req, res, next) {
// do logic
if () {
return res.send(); // or res.somethingThatSendsOutAHttpResponse()
}
// next middelware
next();
};
Since express routes are also middlewares, (mind blown), you can mount them top down.
To authenticate a route, just put the auth.js middleware on top of that route.
router.get('/', require('./auth'));
router.get('/', require('./log'));
router.get('/', (req, res, next) => {
// yolo
});
Now since this is web dev, you still got problems.
Now all your boring database queries are scattered everywhere.
Fear not, you can solve it, by, guess, creating another file.
apiRoutes.get('/userfiles', function(req, res) {
const userFile = require('/path/to/model/with/userfile/methods/exported/out');
// do something with userFile's methods
});

How can I still use a router in node express and still have a wildcard and params?

So basically I want my code being able to go mywebsite.com/username and itll take them to their profile.
My app.js has -
var user = require('./routes/user');
app.use('/*', user);
and then in my user.js I have
router.get('/:username', function(req, res) {
console.log('the user name', req.params.username);
})
If I change router.get to just router.get('/') my console.log logs out undefined and if I leave it as router.get('/:username') I get a 404.. I also tried doing app.use('/:username', user) as well but that still didn't work.
You should't need to have a wildcard like you do on app.use
Try something like this.
Also see the express router documentation http://expressjs.com/en/guide/routing.html
// Required Moduiles
var express = require('express');
var router = express.Router();
var app = express();
// Add route to get
router.get('/:username', function(req, res) {
res.status(200).send(req.params.username)
})
// Add username
app.use('/', router)
// Start express server
app.listen(3030);
Update -
Just using app.use(router) would do the same thing if all your routes are going to be contained in this new router instance / middleware you are creating in a separate file.

Encapsulate Express Routers

Is it possible to create different routers using Express.Router that don't share middleware?
To me it seems that Express.Router uses a singleton, so no matter what I try, the middleware gets attached to all routers. So, without having to create multiple instances of the Express app, is there a way to achieve the following:
Create mutliple routers
var router_a = Express.Router();
var router_b = Express.Router();
Give each router unique routes and middleware
router_a.use(function(req, res, next){
console.log('Only works on router_a!');
});
router_a.get('/', function(req, res){
console.log('Only works on router_a!');
});
router_b.use(function(req, res, next){
console.log('Only works on router_b!');
});
router_b.get('/', function(req, res){
console.log('Only works on router_b!');
});
Attach each route to a custom url namespace
app.use('/a', router_a);
app.use('/b', router_b);
Is there a straight forward way to achieve this? After reading through the docs on the Router I don't see anything that suggests such is possible.
The one thing I see missing from your code is the call the next() in your middleware. If I add that to your code, it works perfectly fine for me.
The /b middleware is only called if the route starts with /b and same for the /a middleware with /a routes. And, to finish your code, you also have to send a response in your .get() handlers.
Here's the specific code I just tested:
var express = require('express');
var app = express();
var server = app.listen(80);
app.use(express.static('public'));
var router_a = express.Router();
var router_b = express.Router();
router_a.use(function(req, res, next){
console.log('.use() - Only works on router_a!');
next();
});
router_a.get('/', function(req, res){
console.log('.get() - Only works on router_a!');
res.send("router a, / route");
});
router_b.use(function(req, res, next){
console.log('.use() - Only works on router_b!');
next();
});
router_b.get('/', function(req, res){
console.log('.get() - Only works on router_b!');
res.send("router b, / route");
});
app.use('/a', router_a);
app.use('/b', router_b);

Move routes into files in Express.js

Say I have some routes (I have a lot more, but this should explain):
router.post('/post');
router.get('/post/:id');
router.get('/posts/:page?');
router.get('/search');
For the /post ones I know I could do something like
app.use('/post', postRoutes)
Where postRoutes is the actual post routes in another file. However, I'd like to group all post related routes into a postRoutes component (so /post and /posts), search into a search component and so on. Is there a way to do something like
router.use(postRoutes); // includes routes 1-3 above
router.use(searchRoutes); // only the 4th route above
And so on? That would let me keep the top level file much cleaner.
Yes it is simple. You can even make more nesting levels. I think it is good to separate routes, especially when you have dozens of routes.
in your first file (server.js)
app.use(require("./allpost"));
app.use(require("./allqueries"));
in allpost.js
var express = require('express');
var router = new express.Router();
router.post('/post', function (req, res) {
//your code
});
router.get('/post/:id', function (req, res) {
//your code
});
router.get('/posts/:page?', function (req, res) {
//your code
});
when you want more nesting
router.use(require("./deeper"));
or when you want use path part
router.use("/post2/", require("./messages/private"));
module.exports = router;
You could do that by creating a special route file. Here's an example of such file
module.exports = (function() {
var express = require('express');
var router = express.Router();
router.get("/:id", function (request, response, next) {
request.body.id = request.params["id"];
// Do something ...
});
router.post("/someRoute", function (request, response, next) {
// Do something ...
});
// And so on ...
return router;
})();
Next, in you server.js file, include it like this
app.use('/post', require('./routes/postRoutes'));
The problem was I was thinking about this wrong. First off, don't use singular and plural. It makes it a headache and also makes it hard for people to remember the API.
Once I used all plural I had a setup like this in my index.js file:
// The API routes all start with /api and we pass app here so we can have some
// sub routes inside of api
app.use('/api', require('./app/api')(app));
And then in my api/index.js
var express = require('express');
var router = express.Router({ mergeParams: true });
var routeInit = function (app) {
app.use('sessions', require('./sessions')(router));
app.use('users', require('./users')(router));
return router;
};
module.exports = routeInit;
You can see that I'm passing the router manually each time. Then finally:
var routeInit = function (router) {
router.post('/blah', function (req, res, next) {
// Do stuff
});
return router;
};
module.exports = routeInit;
This allowed me to nest routes infinitely deep.

Resources