Recursive Expressjs Routing - node.js

Without resorting to regular expression is there anyway with expressjs to recursively call routing ie url examples:
/f:forum/s:section/t:thread/p:post
/f:forum/s:section/s:section/t:thread/p:post
/f:forum/s:section/s:section/s:section/t:thread/p:post
...
Therefore allowing technically an infinite amount of "sections/subsections" in the forum.
I attempted to do:
app.js:
var express = require('express');
app = express();
app.route('/').get(function(req, res, next){
return res.send('hello');
});
app.use('/f:forum', require('./section'));
server = app.listen(process.env.http || process.env.PORT);
module.exports = app;
section.js:
var router = require('express').Router();
router = router;
router.route('/s:section').get(function(req, res, next){
return res.send(req.params);
});
router.use('/s:section', require('./thread'));
module.exports = router;
thread.js:
var router = require('express').Router();
router.use('/s:section', require('./section'));
router.route('/t:thread/p-:post').get(function(req, res, next){
return res.send(req.params);
});
router.route('/t:thread').get(function(req, res, next){
return res.send(req.params);
});
module.exports = router;
but interestingly it tells me that in thread.js require('./section') = {}
yet in app.js it is correct... any suggestions?

You can do wildcard routing like router.route('/:path*') and then have the handler parse from that point down.
For example, something like:
router.route('/forum/:path*', function(req,res){
var requestPath = req.path; // will present the whole path to you for parsing
// do whatever db lookup logic you normally would do now that you have the pieces you wanted
res.render('forum', data);
};

Related

How to execute NodeJS function on page refresh

I wrote a small NodeJS application from an example. Heres the part of my code.
server.js:
var app = express();
var helper = require('./helpers.something.js');
helper.calculate(function(result) {
app.set('something', result);
});
var router = express.Router();
var home = require(./controllers/home.js);
router.get('/', home.index);
home.js
exports.index = (function(req, res) {
res.locals.calculations = req.app.get('something');
res.render('home', {
data: req.app.get('something');
});
};
The problem I am trying to resolve now is that helper.calculate function is called only when server is started instead of being called every time the page is refreshed. Could anyone advice me how can I call helper.calculate every time the page is refreshed if I want to use the result in both home.js and server.js files as I'm quite lost in express.js documentation.
You can create a middleware that will run the function every time that this route is accessed.
You might change this:
helper.calculate(function(result) {
app.set('something', result);
});
var router = express.Router();
var home = require(./controllers/home.js);
router.get('/', home.index);
to something like this:
var router = express.Router();
var home = require(./controllers/home.js);
const calculate = (req, res, next) => {
helper.calculate(function(result) {
app.set('something', result);
});
next();
};
router.get('/', calculate, home.index);
OR:
var router = express.Router();
var home = require(./controllers/home.js);
const calculate = (req, res, next) => {
helper.calculate(function(result) {
app.set('something', result);
next();
});
};
router.get('/', calculate, home.index);
depending on whether or not you want to wait with running the controller until the helper.calculate() finishes.
The other option would be to add this to the controller code in home.js but I assume that you don't want to do that because you would use app.set() and app.get() one after another so I guess you want to separate the act of setting the variable and using it.

NodeJS - Multiple Router files

I have 2 router files. One is for view routing and other for api requests.
I am trying to set the routing using:
var routes = require('./routes/index'); //View Router
var api = require('./routes/api'); //API Router
app.use('/', routes);
app.use('/api', api);
This fails in case of /api requests. If I remove one of the routings, the other works.
I also tried,
routes(app);
api(app);
But this fails too. Any idea what might be the issue? Please let me know in case additional details are required.
routers/index.js
var express = require('express');
var router = express.Router();
var Promise = require('bluebird');
var nforce = require('nforce');
var org = require('../lib/connection');
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'App' });
});
router.get('/accounts', function(req, res, next){
console.log(org);
res.render('partials/' + name);
org.query({query: 'Select Id, Name, Type, Industry, Rating From Account Order By LastModifiedDate DESC'})
.then(function(results){
console.log(results);
res.render('accounts', {title: 'Accounts', records: results.records});
});
});
router.get('/partials/:name', function(req, res, next){
var name = req.params.name;
console.log(name);
res.render('partials/' + name);
});
router.get('/api/:name', function(req, res, next){
var name = req.params.name;
console.log(name);
res.render('api/' + name);
});
module.exports = router;
/routers/api.js
var express = require('express');
var router = express.Router();
var Promise = require('bluebird');
var nforce = require('nforce');
var org = require('../lib/connection');
/* GET home page. */
router.get('/getAccounts', function(req, res, next) {
console.log('in API router...');
org.query({query: 'Select Id, Name, Type, Industry, Rating From Account Order By LastModifiedDate DESC'})
.then(function(results){
console.log(results);
res.json({'accounts': results.records});
});
});
module.exports = router;
Maybe wrong but i see problem in this route.
router.get('/api/:name', function(req, res, next){})
It will match /api/cuteName and it also will match /api/getAccounts.
So you need to make routes more clear. I would suggest to change route inside index.js to be all something like /main/.
And all API routes move to api.js.
Hope this helps.

get REST APi response in another route

I'm using Express with generator
I want to use JSON response of Rest Api in another route
like this :
var express = require('express');
var router = express.Router();
router.get('/api/:id', function(req, res, next) {
res.json($something); // generate json object
});
router.get('/show', function(req, res, next){
router.get(/api/12,function(request, response){
res.send(request.body);
});
});
Either you extract the middleware function used for /api/:id/ so you can reuse it (recommended) or you need to issue a proper request. router.get does not request the resource, as you seem to think, it rather sets up a route.
So, I'd go for:
var express = require('express');
var router = express.Router();
var getResource = function(req, res, next) {
//use req.params.id to look up resource
res.json($something); // generate json object
}
router.get('/api/:id', getResource );
router.get('/show', function(req, res, next){
req.params.id = "12";
return getResource(req, res, next);
});

express router with id

I know I can do this in Express:
app.use('/:id', function (req, res){ // or app.get
console.log('Test param: ' + req.params.id); // "Test param: foo"
});
to get my url parameter.
When I try to use a router logic I did:
index.js
var sites = require('./routes/sites');
app.use('/:id', sites);
routes/sites.js
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
console.log(req.params.id); // logs "undefined"
// etc ...
});
module.exports = router;
this logic does not work, I get {} in the shell command, undefined for req.params.id.
Any idea where my logic is wrong?
It doesn't work, because that's just not how express interacts with its Routers. The reason you're not seeing id inside your router, is because :id was not declared within its scope. You'd need to do something like
app.use('/', sites);
and in your router
var express = require('express');
var router = express.Router();
router.get('/:id', function(req, res, next) {
console.log(req.params.id);
// etc ...
});
module.exports = router;
Additionally you can try playing around with app.param() see docs for examples http://expressjs.com/api.html#app.param
When you are trying to do this in index.js and routes.js -
app.use('/:id', function (req, res){ // or app.get
console.log('Test param: ' + req.params.id); // "Test param: foo"
});
router.get('/', function(req, res, next) {
console.log(req.params.id); // logs "undefined"
// etc ...
});
"id" is not defined there because that's how express works with routers, it just matches the routes matching with the specified prefix in app.use() method. For that, you can try using -
const express = require('express');
const router = express.Router({mergeParams: true});
Now it will work perfectly.
It actually works now.
You can do something like
app.use('/:id/transactions', require('./routes/transactions'));
In the main file, and then add {mergeParams:true} to Router options in the controller file.
const router = require('express').Router({mergeParams:true});
So here you will be able to get the id that was set in base Route.
// localhost:5000/:id/transactions/
router.get('/',(req, res) => {
console.log(req.params.id);
}
Found here:
http://expressjs.com/api.html#app.param
In your file router/sites.js, add an option to express router:
const router = express.Router({mergeParams:true});
Now you have access to id in sites.js file.

Pass configuration to controller

I am building a node.js app that will upload files to my S3 bucket using knox. I am able to interact with S3 as expected, but I would like to make my controller to receive configuration so I can dynamically construct my client with configuration values.
My questions is how do I get configuration paramters down the call stack to my controller without being careless?
Disclaimer: I am rather new to Node.js so it could be simply my lack of knowledge in the difference between exports. and module.exports.*
Here is an example of how the interaction works with my code:
app.js
...
config = require('./config/config')['env'];
require('./config/router')(app, config);
...
router.js
module.exports = function(app, config) {
...
var controller = require('../app/controllers/home'); //Is there a way for me to pass config here?
app.post('/upload', controller.upload); //Or here?
...
}
home.js
var knox = require('knox');
var client = knox.createClient({ ... }); //I want to use config.key, config.secret, etc instead of hard-coded values
...
exports.upload = function(req, res) {
//Use client
}
...
Try doing something like this...
var config = require('./config/config')['env'];
// The use function will be called before your
// action, because it is registered first.
app.use(function (req, res, next) {
// Assign the config to the req object
req.config = config;
// Call the next function in the pipeline (your controller actions).
return next();
});
// After app.use you register your controller action
app.post('/upload', controller.upload);
And then in your controller action...
exports.upload = function(req, res) {
//Your config should be here...
console.log(req.config);
}
Ps. I can not try it right now, but I solved a similar issue like this.
You can pass the configuration in as a parameter to you controller
Controller
// controller.js file
module.exports = function(req, res, config) {
console.log('config parameter passed to controller', config);
res.end('config passed')
}
App
// index.js file with the express app
var controller = require('./controller');
var config = {
key1: 'foo'
};
var express = require('express');
var app = express();
var port = 3000;
app.get('/', function(req, res){
controller(req, res, config);
});
app.listen(port);
console.log('app listening on port', 3000);
Demo
You can check out the github repo for a complete example
Alternative approach if you want to call multiple functions from one single route, this will do it.
Route
var users = require('../controllers/users');
app.route('/login').post(function(req, res){
if(users.authenticate()){
console.log('valid user');
if(users.createUser())
{
console.log('user created');
}
}
});
Controller
exports.authenticate = function(req, res, next) {
return true;
};
exports.createUser = function(req, res, next) {
return true;
};

Resources