I want to extract some repetitive code into a single module in Node.js and Express.js
Together.js
var express = require('express');
var boom = require('express-boom');
var app = express();
var app.use(boom());
app.param('user', function(request, reply, next, id){
request.db.users.get(id, function(err, userInfo){
if (err) reply.boom.badImplementation(err);
else if (!userInfo || !userInfo.length) reply.boom.notFound();
else {
request.user = userInfo[0];
next();
}
})
})
app.get('/api/users/:user', function(request, reply){
reply.json(request.user);
});
app.listen(3000);
I have multiple routes I want to use this param conversion including: /users/:user, /api/users/:user, /checkout/:user/:barcode, etc. but each of the root routes (i.e. users, api, checkout) are in their own file and I am attaching them with app.use('/users', userRoutes);. As it is, I will have to put my user param conversion into EACH of these sub-route modules.
I would like to have an interceptors.js where I make all of the common param interceptor functions and only write them once. Here is an example of how I thought it would work.
app.js
var express = require('express');
var app = express();
app.use(require('./routes/interceptors'))
app.use('/users', require('./routes/users'));
app.use('/api', require('./routes/api'));
app.use('/checkouts', require('./routes/checkouts'));
app.listen(3000);
./routes/api.js
var express = require('express');
var api = express.Router();
api.get('/users/:user', function(request, reply){
reply.json(request.user);
});
module.exports = api;
./routes/interceptors.js
var express = require('express');
var boom = require('express-boom');
var interceptors = express.Router();
var interceptors.use(boom());
interceptors.param('user', function(request, reply, next, id){
request.db.users.get(id, function(err, userInfo){
if (err) reply.boom.badImplementation(err);
else if (!userInfo || !userInfo.length) reply.boom.notFound();
else {
request.user = userInfo[0];
next();
}
})
})
module.exports = interceptors;
There would of course be another file for each of checkout.js and users.js and they will be the same principal as api.js
When I do the above, the param interceptor is never run. No errors are throw that I can see.
Thank you all for any help you may provide,
Rhett Lowe
This can't be done.
Param callback functions are local to the router on which they are defined. They are not inherited by mounted apps or routers. Hence, param callbacks defined on app will be trigerred only by route parameters defined on app routes.
http://expressjs.com/api.html#app.param
Another approach you could do is to have a module with your interceptors and require it in your route files where necessary.
./routes/api.js
var express = require('express');
var api = express.Router();
var interceptors = require('./interceptors');
api.use('user', interceptors.user);
api.get('/users/:user', function(request, reply){
reply.json(request.user);
});
module.exports = api;
./routes/interceptors.js
exports.user = function(request, reply, next, id){
request.db.users.get(id, function(err, userInfo){
if (err) reply.boom.badImplementation(err);
else if (!userInfo || !userInfo.length) reply.boom.notFound();
else {
request.user = userInfo[0];
next();
}
})
})
module.exports = interceptors;
Related
here is app.js file:
var express = require('express');
var routes = require('./routes/index');
app = express();
app.use('/', routes);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err); //res.render('404')
});
and routes/index.js:
var express = require('express')
, router = express.Router()
var importer = require('./../lib/importer')
, importRoutes = importer(__dirname)
, routes = {
views: importRoutes('./views')
};
router.get('/login', routes.views.login.get);
router.post('/login', routes.views.login.post);
router.get('/register', routes.views.register.get);
... and many other routes (more than 50)
router.get('/theLastRoute', routes.views.auth.oneOfTheLastRoutes.get);
module.exports = router;
BUT if I go to localhost:3000/oneOfTheLastRoutes or some route from the bottom the app responses 404, even this route really exist in routes/index.js, and if I move this route higher in the module the app will give correct response.
I'm almost sure that the matter is in lib/importer.js where some async code requires all routes from the routes/views folder, and not all routes are assigned to rotutes.view property.
importer.js:
var fs = require('fs');
var path = require('path');
function dispatchImporter (rel__dirname) {
function importer (from) {
//console.log('importing ', from);
var imported = {};
var joinPath = function () {
return '.' + path.sep + path.join.apply(path, arguments);
};
var fsPath = joinPath(path.relative(process.cwd(), rel__dirname), from);
fs.readdirSync(fsPath).forEach(function (name) {
var info = fs.statSync(path.join(fsPath, name));
//console.log(name);
if (info.isDirectory()) {
imported[name] = importer(joinPath(from, name));
} else {
// only import files that we can `require`
var ext = path.extname(name);
var base = path.basename(name, ext);
if (require.extensions[ext]) {
imported[base] = require(path.join(rel__dirname, from, name));
} else {
console.log('cannot require ', ext);
}
}
});
return imported;
}
return importer;
}
module.exports = dispatchImporter;
AND I didn't managed to come up with a solution, where all route files would be required and assigned to routes object before the 404 handler.
the structure of files is as follows:
app.js
lib/
importer.js
routes/
index.js
views/
login.js
register.js
etc...
the problem turned out to be in some other module, where overlapping route was declared.
I know there are lots of questions similar to mine but I could not find the best solution.
I am creating a web app with node and rethinkdb. I want to organise different js files (modules) so that each has specific task.
I have this query.js file whose query result must be passed to routes.js file.
I have tried implement this in the following way.
query.js
//dependencies
var express = require('express');
var path = require('path');
var r = require('rethinkdbdash')({
port: 28015,
host: 'localhost',
db: 'stocks'
});
var len;
//function to get companies list
exports.clist = function(){
r.table('company')
.run()
.then(function(response){
return response;
})
.error(function(err){
console.log(err);
})
}
console.log(exports.clist[0].id)
//function to get number of entries in database
exports.clen = function(){
r.table('company')
.run()
.then(function(response){
len = Object.keys(clist).length;
return len;
})
.error(function(err){
console.log(err);
})
}
routes.js
//dependencies
var express = require('express');
var request = require('request');
var path = require('path');
var r = require('rethinkdbdash')({
port: 28015,
host: 'localhost',
db: 'stocks'
});
//query module
var query = require('./query')
clist = query.clist();
clen = query.clen();
//create router object
var router = express.Router();
//export router
module.exports = router;
//home page
router.get('/', function(req, res) {
console.log('served homepage');
res.render('pages/home');
});
//--companies page--//
router.get('/company', function(req,res){
console.log('served companies page')
res.render('pages/company', {
clist: clist,
x:clen
});
});
the console log in query.js is showing that cannot read property id of undefined.
Also I would like to know is there a way to directly pass the variables instead of using functions and then calling it.
I apologise if the solution is obvious.
To summarise I want the query result which is an object to be accessible from routes.js file.
Note: As exports.clist1 is an asynchronous method, you can't expect the result to be printed in the next line, hence comment this line and follow as below
//console.log(exports.clist[0].id)
You have to register a middleware to make this working, otherwise, query will be called only at the time of express server started and not at every request.
So you can do like this,
Hope you had something like this in your startup file (app.js),
var app = module.exports = express();
routes.js
//query module
var query = require('./query')
var app = require('../app'); // this should resolve to your app.js file said above
//clist = query.clist();
//clen = query.clen();
// middleware to populate clist & clen
app.use(function(req, res, next){
query.companyList(function(err, data){
if(!err) {
req.clist = data.clist;
req.clen= data.clen;
}
next();
});
});
query.companyList(function(err, data){
if(err) {
console.log(err);
} else {
console.log(data.clist[0].id);
console.dir(data.clist);
}
});
//create router object
var router = express.Router();
//export router
module.exports = router;
//home page
router.get('/', function(req, res) {
console.log('served homepage');
res.render('pages/home');
});
//--companies page--//
router.get('/company', function(req,res){
console.log('served companies page')
res.render('pages/company', {
clist: req.clist,
x: req.clen
});
});
Change your query.js like this,
//function to get companies list
exports.companyList = function(next){
r.table('company')
.run()
.then(function(response){
var list = {
clist: response,
clen: Object.keys(response).length
};
next(null, list);
})
.error(function(err){
console.log(err);
next(err);
})
};
I want to make a change from the actual structure of my code. This is the actual code i have:
//index.js
var routes = require('./routes');
var subdomain = require('express-subdomain');
//require express app with settings
var app = require('./app');
//export the application
module.exports = app;
// routes request like endusers-api.mydomain.ext/
app.use(subdomain('endusers-api', routes.apis.endusers));
// routes request like mydomain.ext/
app.use(routes.webapps.endusers);
//routes/index.js
var apis = {endusers: require("./apis/endusers")}
var webapps = {endusers: require("./webapps/endusers")}
var routes = {apis: apis, webapps: webapps}
module.exports = routes;
//routes/apis/endusers
var express = require('express');
var route = express.Router();
var logger = require('../../lib/logger');
route.get('/', logger("endusers-api-access"), function(req, res, next) {
res.json({
"name" : "Endusers API"
});
});
module.exports = route;
//routes/webapps/endusers.js
var express = require('express');
var route = express.Router();
var logger = require('../../lib/logger');
route.get('/', logger("endusers-webapp-access"), function(req, res, next) {
res.render('endusers/index', {title: 'Homepage'});
});
module.exports = route;
Now I want to change the above code to this (feel free to tell me if this is a good approach of doing things in Node.js or not):
//index.js
var middlewares = require('./middlewares');
var app = require('./app');
module.exports = app;
//i want to change to this
app.use(middlewares.endusersApi);
app.use(middlewares.endusersWebapp);
//Stuff for creating server and listening...
//middlewares/index.js
var middlewares = {
endusersApi : require("./apis/endusers"),
endusersWebapp : require("./webapps/endusers")
}
module.exports = middlewares;
//middlewares/apis/endusers.js
//TODO
//middlewares/webapps/endusers
//TODO
How should I write the TODO portions above. It look like we will need nested middlewares (a middleware calling another middleware). Please, your suggestions.
I found the answer using by express.Router().all() method.
//middlewares/apis/endusers.js
var routes = require('../../routes');
var express = require('express');
var middleware = express.Router();
middleware.all('/',subdomain('endusers-api', routes.apis.endusers));
//the two next lines are alternatives to the line above
//middleware = subdomain('endusers-api', routes.apis.endusers); //can assign only one route
//middleware.use(subdomain('endusers-api', routes.apis.endusers)); // can use many routes
module.exports = middleware;
//middlewares/webapps/endusers.js
var routes = require('../../routes');
var express = require('express');
var middleware = express.Router();
middleware.all('/', routes.webapps.endusers);
//the two next lines are alternatives to the line above
//middleware = routes.webapps.endusers; //can assign only one route
//middleware.use(routes.webapps.endusers); // can use many routes
module.exports = middleware;
I'm just now started to learn Node and Express, I have some probelm with the routes in express. I want a well modular code for routing. I want to query some data from mysql database:
Here is my app.js(it is on "public_html" directory):
var path = require('path');
var express = require('express');
var routes = require('./routes');
var app = express();
var staticPath = path.resolve(__dirname, './');
app.use(express.static(staticPath));
routes.init(app);
module.exports = app;
app.listen(3000, function() {
console.log('Server is listening on port: 3000');
});
Next file: "public_html/routes/index.js":
exports.init = function(app){
//electronics routes
app.use('/laptop', require('./laptop'));
};
"public_html/routes/laptop/index.js":
var router = require('express').Router();
router.get('/laptop', require('./modules/laptop'));
module.exports = router;
"public_html/routes/laptop/modules/laptop.js":
var mysql = require('mysql');
var connection = mysql.createConnection(
{
host : 'localhost',
user : 'admin',
password : 'xxxxxxx',
database : 'database',
}
);
module.exports = function(req, res){
connection.connect();
var queryString = 'SELECT * FROM laptop';
connection.query(queryString, function(err, rows, fields) {
if (err) throw err;
res.writeHead(200, { 'Content-Type': 'application/json'});
res.end(JSON.stringify(rows));
});
connection.end();
};
I want to keep this modularity even if its look like a bit over complicated, in the future I will need it.
So the problem is it's just doesn't working, I think its just a stupid error somewhere, but I don't find it and on the internet there is only simple route examples so I cant use them in this case.
The problem is most likely in your routing.
You're mounting a separate router at /laptop. Within that mounted router you're setting up a /laptop (GET) route on top of that, so the full path would be /laptop/laptop.
I want to use different routes in my app, depending on the subdomain in req.headers.host.
So I came around with this idea (extremely simplified example):
var express = require('express');
var domain1 = require('./routes/domain1');
var domain2 = require('./routes/domain2');
var app = express();
app.use('*', domainRouting);
function domainRouting(req, res, next){
var subdomain = req.headers.host.split('.')[0];
if(subdomain === 'domain1'){
app.use(domain1);
}
else{
app.use(domain2);
}
next();
}
//404 handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
res.send('error');
});
var server = app.listen(3001, function() {
console.log('Listening on port %d', server.address().port);
});
domain1.js:
var express = require('express');
var router = express.Router();
router.get('/', function(req, res) {
res.send('DOMAIN 1: ' + req.url);
});
module.exports = router;
domain2.js:
var express = require('express');
var router = express.Router();
router.get('/', function(req, res) {
res.send('DOMAIN 2: ' + req.url);
});
module.exports = router;
But this does not work, the routes are ignored and the request jumps into the last 404-handler.
Any ideas for this?
You can't use app.use() dynamically like that within a middleware. You might call your router directly with domain1(req, res, next) instead of app.use(domain1).
Or you might look into using a module like subdomain to make it easier to handle subdomains in Express.