I have an NodeJS Express app that is getting really big in just one file (app.js).
I want to export all my routes into a single, external file, say ./lib/routes.js. How to do that?
How to export the following bit of code and require it correctly in main app.js?
app.get('/logout', function(req, res) {
res.render('logout', {
username: req.session.username
});
});
app.get('/dashboard', function(req, res) {
res.render('dashboard', {
username: req.session.username
});
});
app.get('/login', function(req, res) {
res.render('login', {
badLogin: false,
loginError: false
});
});
What I do is group my routes by controller. For each group of related routes (users, shopping cart, whatever), I make a controller file that lives in app/controllers/foo.js where foo is the controller name. In my main javascript server file (where all your code currently lives), I require each controller by name and then call its setup function, passing in my express app object, and allow the controller to add whatever routes it needs.
['api', 'authorization', 'users', 'tests'].map(function(controllerName) {
var controller = require('./controllers/' + controllerName);
controller.setup(app);
});
Inside each controller, I do something like:
exports.setup = function(app) {
app.get('/dashboard', function(req, res) {
res.render('dashboard', {
username: req.session.username
});
});
};
Why not do something like this:
// logout.js
module.exports = function(req, res){
res.render('logout', {
username: req.session.username
});
});
// dashboard.js
module.exports = function(req, res){
res.render('dashboard', {
username: req.session.username
});
});
// login.js
module.exports = function(req, res){
res.render('login', {
badLogin: false,
loginError: false
});
});
// app.js
app.get('/logout', require('logout'));
app.get('/dashboard', require('dashboard'));
app.get('/login', require('login'));
Also, you could imagine easily using http://nodejs.org/docs/v0.4.8/api/fs.html#fs.readdir to loop through a routes directory and load these up programmatically.
You could even do something along the lines of...
module.exports.handler[] = {
method : 'get',
route : '/login',
action : res.render('login', {
badLogin: false,
loginError: false
});
}
Though I think I'd spend a little time thinking about how to simplify that.
using glob you can export all routes on directory for example '/routes':
npm i --save glob
// *** /routes/index.js file ***
const express = require('express')
const Router = express.Router
const router = Router()
const glob = require('glob')
/**
* options ignore files inside routes folder
*/
const options = {
ignore: [`${__dirname}/_helpers.js`, `${__dirname}/index.js`]
}
/**
* read all files on current directory and export routes as lowercase of the filename
* example 'routes/Products.js' route will be access by '/products'
*/
const routes =
glob.sync(__dirname + '/*.js', options)
.map(filename => {
const arr = filename.split('/')
let name = arr.pop();
name = name.replace('.js', '')
return {
path: `/${name.toLowerCase()}`,
router: require(`${filename.replace('.js', '')}`)
}
})
.filter(obj => Object.getPrototypeOf(obj.router) == Router)
.forEach(obj => router.use(obj.path, obj.router))
module.exports = router;
then
on app.js
// app.js file
const express = require('express')
const routes = require('./routes')
const app = express()
app.use('/api', routes)
Related
I'm very new to node.
I want to apply custom authentication using passport.js to all router.
using this example. -> https://github.com/passport/express-4.x-http-bearer-example
the code below is my server/index.js.
const express = require('express');
const path = require('path');
const index = require('./routes/index.js');
const downloadRouter = require('./routes/fileDownload.js');
const app = express();
app.disable("x-powered-by");
app.use('/', express.static(path.join(__dirname, '..', 'dist')));
app.set("src", path.join(__dirname, "../src"));
var passport = require('passport');
var Strategy = require('passport-http-bearer').Strategy;
var db = require('./adminDB');
passport.use(new Strategy(
function(token, cb) {
db.users.findByToken(token, function(err, user) {
if (err) { return cb(err); }
if (!user) { return cb(null, false); }
return cb(null, user);
});
}));
// app.use('/api-test', passport.authenticate('bearer', { session: false
}), function(req, res) {
// res.json({ username: req.user.username, value:
req.user.emails[0].value });
// res.end();
// });
app.use('*', passport.authenticate('bearer', { session: false }),
function(req, res, next) {
console.log("api all before action")
if(!err) {
next();
} else {
res.status(401).end();
}
});
app.use('/download', downloadRouter);
const { PORT = 8080 } = process.env;
app.listen(PORT, () => console.log(`Listening on port ${PORT}`));
// export default app;
what I want to ask is this part.
The annotated api-test parts handles authenication very well. However, the "app.use(*)" part does not handle it. console.log is also not working. Returns 200, regardless of the certification procedure, outputting the original screen(index.html).
How do I authenticate across all my routers and get all of my screens back to their original output?
I leave an answer because there may be someone in a similar situation.
app.use('*', passport.authenticate('bearer', { session: false }), function(req, res, next) {
next();
});
put this code, upper then router code.
When my application starts, an object is created that is used throughout the app in multiple files. In my app.js file I am requiring several files that contain my routes. These routes need access to the object created in app.js so I'm trying to pass it to each of these files. An example of one of the files is below.
test.js
const express = require('express');
const router = express.Router();
router.use('/api', router);
router.post('/testRoute', (req, res, next) => {
if(globalVariable.abc) { //globalVariable is the variable created in app.js
//do some logic
}
res.json({
message: "Test Route Success"
})
});
module.exports = router;
app.js
const assessmentRoutes = require('./routes/test');
app.use(assessmentRoutes);
One way I've tried to get the variable passed in is wrapping the routes in test.js in a function that takes the variable like this.
module.exports = function(globalVariable) {
router.post('/testRoute', (req, res, next) => {
if(globalVariable.abc) { //globalVariable is the variable created in app.js
//do some logic
}
res.json({
message: "Test Route Success"
})
});
}
Then in app.js the require changes to:
const assessmentRoutes = require('./routes/assessments')(globalVariable);
When I do it this way I get errors starting the application like app.use() requires a middleware function.
How to I pass the object into my routes?
🙅 Using the global object
One approach, which I don't recommend, is using the global object:
app.js
const assessmentRoutes = require('./routes/test');
global.globalVariable = { abc: ... };
app.use(assessmentRoutes);
test.js
const express = require('express');
const router = express.Router();
router.post('/testRoute', (req, res, next) => {
if (globalVariable.abc) { ... }
res.json({
message: 'Test Route Success',
});
});
module.exports = router;
✨ Export a function and pass in options
Another approach is to export a function where you can pass in that as a param, as you did. The problem is that you are not returning the router from that function, that you later need to use in app.js:
app.js
const assessmentRoutes = require('./routes/test');
app.use(assessmentRoutes({ abc: ... }));
test.js
const express = require('express');
const router = express.Router();
module.exports = function(globalVariable) {
router.post('/testRoute', (req, res, next) => {
if (globalVariable.abc) { ... }
res.json({
message: 'Test Route Success',
});
});
// 👇 This is what you were missing in your code!
return router;
};
✨ Export the router and a setter function
A similar option would be to export the router object as well as a setter function to initialise globalVariable inside test.js:
app.js
const { assessmentRoutes, initAssessmentRoutes } = require('./routes/test');
initAssessmentRoutes({ abc: ... });
app.use(assessmentRoutes);
test.js
const express = require('express');
const router = express.Router();
let globalVariable = { abc: null };
function initAssessmentRoutes(options) {
globalVariable = options;
}
router.post('/testRoute', (req, res, next) => {
if (globalVariable.abc) { ... }
res.json({
message: 'Test Route Success',
});
});
module.exports = {
assessmentRoutes: router,
initAssessmentRoutes,
};
In your test.js get rid of:
router.use('/api', router);
In your app.js change this line:
app.use('/', assessmentRoutes);
This should works.
APP.JS
app.locals.globalVariable= {type:"Fiat", model:"500", color:"white"};
TEST.JS
if (req.app.locals.globalVariable.type =="Fiat" ) {
.....
}
I am building an API backend with Express (v4) and facing an issue that my middleware function is not called
on sub-paths of my route. E.g. it is called for /movie but not for /movie/search.
I have split my routes into separate files. Below is the code, shortened to the relevant parts.
Any help is appreciated!
app.js
var express = require('express');
var app = express();
var router = require('routes')(app);
/routes/index.js
module.exports = function(app) {
app.use('/movie', check_authentication, require('movie'));
};
/routes/movie.js
var Movie = require(../models/movie');
// Middleware is working for this route (/movie?movie_id=123)
router.get('/', function(req, res) {
Movie.findById(req.query.movie_id)
.then(function(movie) {
res.status(200).json(movie);
}, function(err) {
res.status(400).send(err);
});
});
// Middleware is NOT working for this route (/movie/search?keyword=matrix)
router.get('/search', function(req, res) {
Movie.findById(req.query.keyword)
.then(function(movie) {
res.status(200).json(movie);
}, function(err) {
res.status(400).send(err);
});
});
/routes/check_authentication.js
var express = require('express');
var router = express.Router();
var firebaseAdmin = require('firebase-admin');
var path = require('path');
var config = require(path.resolve(__dirname, '../config/config.json'));
firebaseAdmin.initializeApp({
credential: firebaseAdmin.credential.cert(path.resolve(__dirname, '../config/' + config.firebase.serviceAccount)),
databaseURL: config.firebase.databaseURL
});
// AUTHENTICATION MIDDLEWARE
// needs to be included in any request which requires authorization
// =============================================================================
router.all('/', function(req, res, next) {
// check if authorization header is present
var token = req.headers['authorization'];
if (typeof token === 'undefined') {
res.status(403).json({ Error: 'Unauthenticated' });
}
else {
firebaseAdmin.auth().verifyIdToken(token).then(function(decodedToken) {
req.email = decodedToken.email;
next(); // all good. go ahead with the request
}).catch(function(error) {
res.status(403).json({ Error: 'Unauthenticated' });
});
}
});
module.exports = router;
It seems I found the problem.
Changing the middleware to trigger on * fixes it.
router.all('*', function(req, res, next)
Maybe someone can confirm that this is the way to go.
The check_authentication module should export the middleware function, not a router.
module.exports = function(req, res, next) {
// check if authorization header is present
// ...
});
I just started to learn node.js with express 4. I have read some books and tutorials, I also cloned some sample apps from git but I still have a very basic question, which practice should I follow to write the routing(or controller)?
Some people define all the routes in app.js, and export all the functions in the controller:
app.js
....
var homeController = require('./controllers/home');
var userController = require('./controllers/user');
....
app.get('/', homeController.index);
app.get('/login', userController.getLogin);
app.get('/logout', userController.logOUT);
app.get('/doStuff', userController.doStuff);
then in controllers/user.js
exports.getLogin = function(req, res) {
//logic...
});
exports.logout = function(req, res) {
//logic...
});
exports.doStuff = function(req, res) {
//logic...
});
Another way is like express-generator way:
app.js
...
app.use('/users', users);
...
controllers/users.js
....
router.get('/login', function(req, res, next) {
//logic...
});
router.get('/logout', function(req, res, next) {
//logic...
});
router.get('/doStuff', function(req, res, next) {
//logic...
});
module.exports = router;
And other are more dynamic like this proposal
is there any technical difference? Which pattern should I follow?
This is completely preferential. Any pattern that works is likely to be valid here. Express routers make things very nice and easy to setup. Personally I prefer to create a directory for every top level route, files for the second level, and exports for the third. Here's an example of how I lay things out for a set of API routes.
Directory:
routes/
index.js <- master route manifest
api/
index.js <- api routes manifest
books.js
authors.js
landing-pages/
index.js
awesome-deal.js
Route manifest:
// routes/index.js
var router = require('express').Router();
router.use('/api', require('./api'));
router.use('/landing', require('./landing-pages'));
module.exports = router;
API routes manifest:
// routes/api/index.js
var router = require('express').Router();
router.use('/books', require('./books.js'));
router.use('/authors', require('./authors.js'));
module.exports = router;
Entity endpoints:
// routes/api/books.js
var router = require('express').Router();
var db = require('mongoose-simpledb').db;
router.get('/get/:id', function (req, res) {
var id = req.param('id');
db.Book.findOneById(id, function (err, book) {
if (err) throw err;
res.json(book);
});
});
router.post('/new', /* etc... */);
return router;
Then in my app file I only setup the the top-level route:
// app.js
/* express setup.... */
app.use('/', require('./routes'));
I am getting my hands on node.js and I am trying to understand the whole require/exports thing. I have the following main app.js file:
/app.js
var express = require('express'),
http = require('http'),
redis = require('redis'),
routes = require('./routes'),
var app = express(),
client = redis.createClient();
// some more stuff here...
// and my routes
app.get('/', routes.index);
then, I have the routes file:
exports.index = function(req, res){
res.render('index', { title: 'Express' });
};
I can of course use the client object on my app.js file, but how can I use the same object in my routes?
Since req and res are already being passed around by Express, you can attach client to one or both in a custom middleware:
app.use(function (req, res, next) {
req.client = res.client = client;
next();
});
Note that order does matter with middleware, so this will need to be before app.use(app.router);.
But, then you can access the client within any route handlers:
exports.index = function(req, res){
req.client.get(..., function (err, ...) {
res.render('index', { title: 'Express' });
});
};
The easiest way is to export a function from your routes file that takes a client, and returns an object with your routes:
exports = module.exports = function (client) {
return {
index: function (req, res) {
// use client here, as needed
res.render('index', { title: 'Express' });
}
};
};
Then from app.js:
var client = redis.createClient(),
routes = require('./routes')(client);