Express js routes url to correct page - javascript

URL pattern is www.example.com/product-name/pid_010101. URL's first segment is Product name and second segment is Product number. My app router code is below
app.get("/:name?/:id", routes.index);
Now my all urls redirect to same page. For example some url like www.example.com/homepage/banner.html also redirect to www.example.com/product-name/pid_010101
Need to add some Filters in router. How to route the url to correct page?

I assume your routes will be:
/coffeemaker/pid_0101222
/bluemarker/pid_121121
etc. ?
You can use regular expressions for this OR
this is an example of in method filtering:
app.get('/rest/:collection', function(req, res) {
return routes.rest[req.params.collection](req, res);
});
in routes object:
exports.rest = {
tweets: function(req, res) {
return twitter.data.load(function(data) {
return res.json(data);
}, config.site.tag, req);
},
pics: function(req, res) {
return instagram.data.load(function(data) {
return res.json(data);
}, config.site.tag, req);
},
repos: function(req, res) {
return github.data.load(function(data) {
return res.json(data);
}, req);
},
links: function(req, res) {
return delicious.data.load(function(data) {
return res.json(data);
}, config.site.tag, req);
}
};
:colection is then tweets, pics , or links string
Can you make a list of all your pages?
Maybe I can help you with your routes...

This is because of /homepage/banner.html complies to your route as well.
Therefore you must also specify those routes you want to trigger.
Say for example you have a welcome page.(/welcome/guest) or something like that.
You can add another route above the general route. specific for that page.
app.get("/welcome/guest", routes.welcome);
Now this is too much work for all your pages. So you can avoid this with a couple of techniques, one would be to put a static part in the url say:
app.get("/product/:name?/:id", routes.product);

I don't really get your problem. Is /homepage/banner.html a static page? Middlewares work like a filter, you can simply put express.static above express.router:
app.use(express.static('public'));
...
app.use(app.router);
app.get("/:name?/:id", routes.index);
banner.html is in (APP_DIR)/public/homepage/banner.html

Write own middleware function that handles the legacy URLs, and place it above the Express router.
app.use(function(req, res, next) {
if (legacySystemHandles(req.url)) {
// do legacy stuff
}
else next(); // pass to the next middleware function
});
app.use(app.router);

Related

Handle all routes with a prefix in separate js file

I have multiple routes in my express application for different prefix. Each prefix's routes are defined in separate files.
const routes = require('./server/routes');
app.use('/api', routes.apirouter);
app.use('/', routes.webrouter);
where './server/routes.js' is:
module.exports.apirouter = require('./api');
module.exports.webrouter = require('./webroutes');
Hence currently, I am handling and defined all routes with /api prefix in 'api.js' and all other routes are defined in 'webroutes.js'
Now similarly I need to define all the routes with prefix 'fetch-' to a separate js file 'fetch.js', hence http://localhost/fetch-one and http://localhost/fetch-two need to be defined in fetch.js
However the following code is not working for /fetch-one:
const routes = require('./server/routes');
app.use('/api', routes.apirouter);
app.use('/', routes.webrouter);
app.use('/fetch-*', routes.fetchrouter);
routes.js:
module.exports.apirouter = require('./api');
module.exports.webrouter = require('./webroutes');
module.exports.fetchrouter = require('./fetch');
fetch.js:
Route defined for /fetch-one and /fetch-two separately in fetch.js
var fetchRouter = require('express').Router();
fetchRouter.get('/fetch-one', function(req, res, next) {
// localhost/fetch-one not passed control here
});
fetchRouter.get('/fetch-two', function(req, res, next) {
// localhost/fetch-two not passed control here
})
module.exports = fetchRouter;
The problem is that once you've done this:
app.use('/fetch-*', routes.fetchrouter);
Then, the /fetch-* part of the path has been removed from the routing for the fetchrouter. So, when you then do:
fetchRouter.get('/fetch-one', ...)
That won't match because /fetch-one has already been removed from the routing path. The URL would have to have been /fetch-xxx/fetch-one for that to match.
The simplest design would be to change your paths so that the URLs are /fetch/one and /fetch/two which is much more in line with how Express routers work. Then you'd go with:
app.use('/fetch', routes.fetchrouter);
And, have routes in that router for
app.get('/one, ...)
app.get('/two, ...)
That's the URL design that lines up the cleanest with the way Express routers work the simplest.
If you're going to stay with the /fetch-one URL design, then another idea would be to let the fetchRouter look at all top level URLs:
app.use('/', fetchRouter);
And, then have it only have routes for the top level routes you want it to look at. Express will then continue look for other routes that match if it doesn't handle things:
app.get('/fetch-one', ...);
app.get('/fetch-two', ...);
You need to make sure there are no greedy top level routers that take all requests and make sure that this router only takes the request that it needs so that other top level routes get a chance to get matched.
If you really want to stay with the /fetch-* design for the router, then you can do a bit of your own routing and URL comparison:
app.use('/fetch-*', routes.fetchrouter);
Then, in the fetchrouter:
app.get("/", function(req, res, next) {
switch(req.baseUrl) {
case "/fetch-one":
// process /fetch-one here
break;
case "/fetch-two":
// process /fetch-two here
break;
default:
next();
}
});
I thought of one other option that uses the Express parameters where you would just use a handler function for the fetch routes instead of a router:
app.use('/fetch-:id', routes.fetchHandler);
Then, in the fetchHandler:
function fetchHandler(req, res, next) {
switch(req.params.id) {
case "one":
// process /fetch-one here
break;
case "two":
// process /fetch-two here
break;
default:
next();
}
});
Instead of a big switch, you can make it table driven too which is probably cleaner if you have a lot of routes:
app.use('/fetch-:id', routes.fetchHandler);
Then, fetchHandler would be an exported function:
const routeTable = {
one: routeOne,
two: routeTwo,
three: routeThree,
....
};
function fetchHandler(req, res, next) {
let fn = routeTable[req.params.id];
if (fn) {
fn(req, res, next);
} else {
next();
}
});
function routeOne(req, res, next) {
...
}
function routeTwo(req, res, next) {
...
}
function routeThree(req, res, next) {
...
}

Making a get request from within a get request

I'm pretty new to node.js and express and I was wondering if there's a way to define a route that calls upon another route simply to collect data and not to completely reroute.
I've got a route set up as follows:
app.get("/databases/list", function(req, res) {
db.listDatabases().then(names => {
res.send(names);
});
});
Subsequently I'd like to have a different route, say:
app.get('/whatever', function(req, res) {
// here I'd like to make a call to retrieve the information from the first route
// then I'd like to do something with that information, I want to stay in the same route.
}
Is this possible?
Expanding #marcobiedermann answer, In your case simply make a controller and and use the FUNCTION in both the routes. You don't need to fetch anything.
/// --- Controller ----
class SimpleController {
constructor(db){
this.db = db;
}
listDatabase(/*maybe optional callback*/){
return this.db.listDatabases();//or something....
}
whatever(/*maybe optional callback*/){
return this.listDatabase()
.then(process)
}
}
/// --- Routes ----
const sController = new SimpleController(db);
app.get("/databases/list", function(req, res) {
sController.ListDatabase().then(names => {
res.send(names);
});
});
app.get('/whatever', function(req, res) {
sController.whatever()
.then(....)
}
Yes this is possible.
You have to fetch the data from your first endpoint.
fetch('/databases/list')
.then( … )
This requires the /databases/list route to be defined before your /whatever route.
However, I would strongly advice you to NOT do this.
You should abstract your logic into a controller and call this controller in both of your routes:
const fetchController = {
fetchData: () => {
return fetch('path/to/data/to/fetch')
.then( … )
// or database call or wherever you might get the data from
}
}
app.get('/databases/list', (req, res) => fetchController.fetchData());
app.get('/whatever', (req, res) => fetchController.fetchData());
app.get("/databases/list", async function(req, res) {
return await db.listDatabases();
});
app.get('/whatever', async function(req, res) {
const result = await fetch('path/databases/list');
console.log(result)
});
It might help you, But it's not recommended way. You can create method (common somewhere in the controller) and use that where ever you need.

It is possible to enhance the express.js req and res variables without using a middleware function?

I'm working in a restful service using express.js and i want to enhance the req and res variables so for example you could write something like
app.use(function (req, res, next) {
res.Ok = function (data) {
res.status(200).send(data);
};
res.InternalError = function (err) {
res.status(500).send(err);
};
});
And later
router.get('/foo', function (req, res) {
res.Ok('foo');
})
This will send 'foo' in the body of the response and set the status code to 200 and is working perfectly.
My first question is if it is possible to add such functionality without a middleware function, lets say in a property or the prototype of the app variable?
The second question is if there are performance issues if you add many functionality with middleware functions at the app level. Are this functions attached to the request and response object per request or once on the application startup?
I know the Sails framework already do this but I'm wondering if they use middleware functions as well.
I keep digging and turns out that the request and response object are exposed in express using the __proto__ property.
var express = require('express'),
app = express();
app.response.__proto__.foo = function (data) {
this.status(200).send(data);
};
And later in the router
router.get('/foo', function (req, res, next) {
res.foo('test');
});
This will print test in your browser so it is possible to add functionality without using any middleware.
Note: I'm sure there are some drawbacks to this approach (overwriting express predefined properties, for example) but for testing purposes and adding very simple functionality I think is slightly better in terms of performance.
I'm not aware of any other way than using middleware. But in my opinion you could do the following to achieve nearly the same thing.
// Some Route
router.get('/foo', function(req, res, next) {
// ...
if(err) {
res.status(500);
return next(err);
}
return res.send('ok');
});
// Another route
router.get('/bar', function(req, res, next) {
// ...
if(badUserId) {
res.status(400);
return next('Invalid userId.');
}
req.result = 'hello';
return next();
});
router.use(function(req, res) {
// I prefer to send the result in the route but an
// approach like this could work
return res.send(req.result);
});
// Error Middleware
router.use(function(err, req, res, next) {
if(res.statusCode === 500) {
// Log the error here
return res.send('Internal server error');
} else {
return res.send(err);
}
});

routing in mean.js (how to include middlware?)

I'm trying to include a middleware (passport-http-bearer) in MEAN.js, however it uses a different routing syntax than Express 4.
Express API sytnax is:
app.get('/', function(req, res){
res.send('hello world');
});
In MEAN.js routes are defined like this:
app.route('/articles')
.get(articles.list)
.post(users.requiresLogin, articles.create);
How do I include a middleware in the MEAN.js router (in my case passport-http-bearer to check for a token)?
http-bearer's example implementation as middleware is:
app.get('/profile',
passport.authenticate('bearer', { session: false }),
function(req, res) {
res.json(req.user);
});
How should I do this in MEAN.js?
For anyone else ending up here trying to figure out how to do this, here is how it can be done:
app.route('/articles')
.get(passport.authenticate('bearer', { session: false }), articles.list)
.post(passport.authenticate('bearer', { session: false }), articles.create);
Or to make it look nicer, the whole auth function could be put in users.authorization.server.controller.js and called woith something like this:
app.route('/articles')
.get(users.requiresToken, articles.list)
.post(users.requiresToken, articles.create);

node.js & express: for loop and `app.get()` to serve articles

I'm working on a node js express application that uses app.get() to serve the different web addresses. So app.get('/',{}); serves the home page, app.get('/login'{ }); serves the login page, etc.
Is it good practice to programatically serve pages using a for loop, like in this example?
var a = ["a", "b", "c"];
a.forEach(function(leg) {
app.get('/' + leg, function(req, res){
res.render('index.ejs', {
word : leg
});
});
});
index.ejs is just
<p><%= word %> </p>
So that site.com/a, site.com/b, and site.com/c are all webpages.
I want to utilize this to run .foreach() on a list of all of the article titles (coming from a database of stored articles).
EDIT: The website allows users to submit posts, which become "articles" in a database. I want this loop to route to new submitted articles after they've been posted. If I want to do app.get('/' + newPageName); for user submitted pages AFTER I've already started the server with node server.js, how is that achieved?
Make use of middlewares to better handle the requests. I assume you will have tens/hundreds of posts, adding routes for them like what you've done, is not so elegant.
Consider the following code; I am defining routes of /posts/:legId form. We will match all requests to this route, fetch the article and render it.
If there are handful of routes to be defined you could make use of regular expression to define them.
// dummy legs
var legs = {
a: 'iMac',
b: 'iPhone',
c: 'iPad'
};
app.get('/posts/:leg', fetch, render, errors);
function fetch(req, res, next) {
var legId = req.params.leg;
// add code to fetch articles here
var err;
if (!legs[legId]) {
err = new Error('no such article: ' + legId);
}
req.leg = legs[legId];
next(err);
}
function render(req, res, next) {
res.locals.word = req.leg;
res.render('index');
}
function errors(err, req, res, next) {
console.log(err);
res.locals.error = err.message;
// render an error/404 page
res.render('error');
}
Hope this helps, feel free to ask me any questions you may have.
No, you should not be generating route handlers like that. This is what route parameters are for.
When you start a path component (folder) with a : in an Express route, Express will match any URL that follows the pattern automatically, and place the actual value in req.params. For example:
app.get('/posts/:leg', function(req, res, next) {
// This will match any URL of the form /post/something
// -- but NOT /post/something/else
if (/* the value of req.params.leg is valid */) {
res.render('index.ejs', { word: req.params.leg });
} else {
next(); // Since the user requested a post we don't know about, don't do
// anything -- just pass the request off to the next handler
// function. If no handler responds to the request, Express
// defaults to sending a 404.
}
});
In the real world, you'd probably determine if the leg param is valid by doing a database lookup, which entails making an async call:
app.get('/posts/:leg', function(req, res, next) {
db.query(..., req.params.leg, function(err, result) {
if (err) next(err); // Something went wrong with the database, so we pass
// the error up the chain. By default, Express will
// return a 500 to the user.
else {
if (result) res.render('index.ejs', result);
else next();
}
});
});

Categories