Dynamic routing with express framework in node - javascript

I'm using express framework 4.0 for my node js server . I was wondering if there was any way to remove routes dynamically at runtime
var express = require('express');
var router = express.Router(/*Options */);
router.get('/', function (req, res)
{
res.render('index', {title: "Home"});
});
router.get('/features', function (req, res)
{
res.render('features', {title: "Features"});
});
//Hook into the routing system
module.exports = function(app,rootPath)
{
app.use(rootPath, router);
};
This is a trivial example, but how could I remove the /features path from the routing table?Additionally is it possible to overwrite this routing path with another should I wish to update the features routing path at a later date ?

AFAIK you can't delete a route dynamically (at least not in a nice way), but you can use a filtering middleware to disallow access to a route when a certain condition is set.
For example:
var allowRoute = true;
var filterMiddleware = function(req, res, next) {
if (allowRoute !== true) {
return res.status(404).end();
}
next();
};
app.get('/features', filterMiddleware, function(req, res) {
res.render('features', { title: 'Features' });
});
You toggle allowRoute to enable or disable access to the route (obviously, depending on the exact use case you could also use properties in req to enable/disable access to the route).
A similar setup could be used to overwrite the route handler with another one, although I'm beginning to wonder what you are trying to accomplish and if overwriting route handlers is the solution for that.

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.

How can I get data from a get function to another function in the same js file in node.js?

I want to get data out of a router.get() function to another function located in the same JS file.
I have a data sent to a get method:
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
Now I want to use title variable in another method:
router.post('/', function(req, res) {
// I want to use the title variable in here
});
Any help would be appreciated.
You just need to change the scope of the variable. Be careful though when expanding the scope of variables. I.e., you would set the title outside of your router callbacks, and then reference it inside. Also, as your web app grows, it's possible you'll have many different pages, each with their own page titles.
The easy way to make this work is to initialize the title outside the route handler:
// Here, I'm using Object.freeze, assuming you want the map to be
// immutable (i.e., not accidentally changed by other parts of the code)
// (const titles = { ... } won't achieve it)
let titles = Object.freeze({ /* Formatted as a "route" --> "title" map */
index: 'Express'
});
router.get('/', function(req, res, next) {
const routeName = 'index'
res.render(routeName, { title: titles[routeName] });
});
router.post('/', function(req, res) {
const routeName = 'index'
// Access your title here:
console.log(titles[routeName]);
});
As an alternative, expressJS allows us to use app.get() and app.set() methods. Usually most expressJS apps start out like this:
let app = express();
You can stash variables associated with the app like this:
app.set('indexTitle', 'Express');
So that way in your route handler, you can access like this:
router.get('/', function(req, res, next) {
res.render(routeName, { title: app.get('indexTitle') });
});
router.post('/', function(req, res) {
// Access your title here:
console.log(app.get('indexTitle'));
});
Yet, an even easier approach may be to let the front-end keep track of all the page titles, and, if the title is absolutely something the back-end needs, the front-end can simply post that in the req.body. Yes, might be overkill, but it would eliminate the need for the server to keep track of it:
router.post('/', function(req, res) {
// Access your title here:
console.log(req.body.pageTitle);
});

Express 4 Routes Using Socket.io

Having a rough time adding Socket.io in my Express 4 Routes. In my routes/index.js I have:
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function (req, res, next) {
res.render('index', { title: 'Express' });
});
router.post('/message', function(req, res) {
console.log("Post request hit.");
// res.contentType('text/xml');
console.log(appjs);
io.sockets.emit("display text", req);
// res.send('<Response><Sms>'+req.body+'</Sms></Response>');
});
module.exports = router;
but io is undefined. I have seen several examples of how to do this, but none that worked for me. Any help would be appreciated.
You need to pass your socket.io variable to the router module so that it has access. You could do this by wrapping your module in a function call.
var express = require('express');
var router = express.Router();
/* GET home page. */
var returnRouter = function(io) {
router.get('/', function(req, res, next) {
res.render('index', {
title: 'Express'
});
});
router.post('/message', function(req, res) {
console.log("Post request hit.");
// res.contentType('text/xml');
console.log(appjs);
io.sockets.emit("display text", req);
// res.send('<Response><Sms>'+req.body+'</Sms></Response>');
});
return router;
}
module.exports = returnRouter;
Then, whever you import this route you would call this function like: require(./routefile)(io)
Here's a good article about creating modules that require being passed a variable: Node.Js, Require and Exports
How about exploiting events? This could add an extra layer of self-awareness to the app, including socket messaging.
Since express's app() inherits from node's eventEmitter, I app.emit('EVENT_NAME', payload) at any point in my route handlers,
and then just handle that with app.on('EVENT_NAME', function(payload){})
with any logic available. I'm using it in my current setup.
This is super old, but I was looking to do the same thing and didn't find a suitable answer. I came up with a good solution using app.set('socketio', io) and req.app.get('socketio') in my route. I was then able to create a reference to the sender's socket in my route without using io.on().
See the answer I added here for the code:
Use socket.io inside a express routes file

I am having trouble understanding Node.js code

I am having trouble understanding this Node.js code:
I understand importation of the module 'express' and using the Router constructor.
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res) {
res.render('index', { title: 'Express' });
});
module.exports = router;
However, I don't understand the purpose of '/', 'index' in the following part:
router.get('/', function(req, res) {
res.render('index', { title: 'Express' });
});
Some help would be greatly appreciated.
The function of 'index' is that it defines which view to render
res.render(view, [locals], callback)
The function of '/' can be explained by
// invoked for any requested passed to this router
router.use(function(req, res, next) {
// .. some logic here .. like any other middleware
next();
});
// will handle any request that ends in /events
// depends on where the router is "use()'d"
router.get('/events', function(req, res, next) {
// ..
});
You can use this page for future reference :
http://expressjs.com/api.html#res.render
If your site is abc.com, then in simple terms, / means the when somebody is coming to your site and he places abc.com in browser then execute the following function.
url is relative, so / means root.
.render('index'... means that render the index.html or index.ejs that you would be having in your project. Rendering means that send it in the response to the browser.
Node is great, but this is one of their few absolutely horrid APIs.
app.get() with a single String argument gets a property previously set via set(). Basic getter / setter stuff.
app.get() with a String and a function means when they do an HTTP GET to this path, call this function.
Bad bad overloading.

Express Middleware to populate a Jade variable for all app.get()'s

I have a Jade file that all of my templates extend called layout.jade. In it I want to be able to have a logout button if the user is currently logged in (this is kept track of in req.session).
So layout.jade will have something like,
-if (loggedin)
a.navButton(href="/logout") Log Out
The route for a page would look something like,
app.get("/foo", function(req, res) {
res.render("foo", {loggedin: req.session.isValidUser});
});
The thing is, I don't want to have to populate the loggedin variable manually in every single route. Is there a way I can use Express middleware to automatically set some default options for the object sent to res.render? Or is there a better method to do this?
Basically I'm asking how I can have some set of variables always sent to templates, as well as the ability to have certain custom variables available in certain templates by manually setting them in the routes.
It seems this is actually a documented feature I just had trouble finding, unless anyone has a better way of doing it; From the latest Express documentation,
app.locals: Application local variables are provided to all templates
rendered within the application. This is useful for providing helper
functions to templates, as well as app-level data.
So in my login success function has,
req.session.username = profile.username;
app.locals.username = profile.username;
My logout function,
app.get('/logout', function (req, res) {
delete app.locals.username;
req.session.destroy();
res.redirect('/login');
});
And finally in layout.jade/all of my templates,
- if(username)
a.navButton(href="/logout") Logout
If you set res.locals.loggedin in the /login route, as hexacyanide suggests, this local will not be available in the route for /foo. res.locals is cleared upon every request.
you could instead try placing this above other routes:
app.all('*', function(req, res, next){
if(req.user){
res.locals.loggedin = true;
res.locals.currentuser = req.user;
};
next();
});
Pretty sure that if you modify req.user during your route, the res.locals.currentuser that you set before won't updated to be the new req.user. but not certain about that.
I actually use a custom render function for each page where I render a template, it looks like this:
function myRender(templateName){
return function(req, res){
res.locals.currentuser = req.user || null;
res.render(templateName);
};
};
and I use it like this:
app.get('/foo'
, function(req, res, next){
res.locals.bar = req.query['bar'] || "";
console.log('passing request to myRender middleware');
next();
}
, myRender('foo-jade-template')
)
This has the advantage of only setting res.locals.currentuser when I am ready to render something, instead of before executing my route. So if I change req.user it is guranteed to have the most recent version at render time.
There is a line of code that is rather useful to you in the Express source:
// merge res.locals
options._locals = self.locals;
Therefore, when you run res.render(), Express will also take any locals that are stored in res.locals and pass them into the render. Therefore, all you have to do is set res.locals.loggedin to true, and then run res.render() as usual.
app.get('/login', function(res, res) {
res.locals.loggedin = true;
});
app.get('/foo', function(req, res) {
res.render('foo', {});
});

Categories

Resources