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'));
Related
I wanted to automate the model and route parts in node.js express.
For example, the index.js of routes originally looked like this.
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', {title:'Express' });
});
router.get('/a1', function(req, res, next) {
res.render('a1', {title:'Repetitive router.get' });
});
router.get('/a2', function(req, res, next) {
res.render('a2', {title:'Repetitive router.get' });
});
router.get('/a3', function(req, res, next) {
res.render('a3', {title:'Repetitive router.get' });
});
module.exports = router;
I don’t want to hard-code this every time
router.get function part
var express = require('express');
var router = express.Router();
var AAA = require('myGetAll');
AAA.add('/a1', a1, {title:'Repetitive router.get' });
AAA.add('/a2', a2, {title:'Repetitive router.get' });
AAA.add('/a3', a3, {title:'Repetitive router.get' });
/* GET home page. */
for(let i = 0; i <AAA.count(); i++){
router.get(AAA.itemPath[i], function(req, res, next) {
res.render(AAA.itemFile[i], AAA.itemParameter[i]);
});
}
module.exports = router;
I would like to do it this way.
Would it be okay to do this?
if not
MVC like express, but
Recommend a framework with better code automation.
Thank you.
There is a good way you could achieve this using route params.
router.get('/:param(a+[1-3])', function(req, res, next) {
const parameter = req.params.param;
res.render(parameter, {title:`Repetitive router.get this ${parameter}` });
});
You can pass an optional regular expression to the route to match the desired param, or just handle the input inside your code logic (in this case i´ve added a regex to match a1-a3)
https://expressjs.com/en/guide/routing.html
Can we define multiple routes in single router file.
e.g : Consider we have company and user tab and I want to define 1 routers file for each tab. All Company related calls should be handled by Company router and User related calls should be handled by User router.
//app.js
app.use('/', require('./routes/user'));
app.use('/api/user/load_user_list', require('./routes/user'));
app.use('/api/user/get_user_detail', require('./routes/user'));
//User.js router
var express = require('express');
var router = express.Router();
//router 1
router.get('/', function (req, res, next) {
//do something here -
});
//router 2
router.get('/api/user/load_user_list', function (req, res, next) {
//do something here
});
//router 3
router.get('/api/user/get_user_detail', function (req, res, next) {
//do something here
});
module.exports = router;
Currently, when app receives call for '/api/user/load_user_list' my "router 1" gets called.
Am I missing out something. To deal with this, I guess I can have single router call and delegate to different function based on request baseUrl.
Any help / suggestion will be appreciated.. Thanks
Instead of :
app.use('/', require('./routes/user'));
app.use('/api/user/load_user_list', require('./routes/user'));
app.use('/api/user/get_user_detail', require('./routes/user'));
Just use :
app.use('/', require('./routes/user'))
app.use('/api/user', require('./routes/user'));
And in your router file rename the routes like so :
//router 2
router.get('/load_user_list', function (req, res, next) {
//do something here
});
//router 3
router.get('/get_user_detail', function (req, res, next) {
//do something here
});
Reason :
When app.use('/api/user/xyz', require('./xyz')) is called, the uri path after api/user/xyz is sent to the router to be matched
What is happening here is, since you have given /api/user/load_user_list in app.use('/api/user/load_user_list', require('./routes/user'));, express will prefix all the routes inside your ./routes/user with /api/user/load_user_list.
The / router 1 in your user.js becomes /api/user/load_user_list + / and /api/user/load_user_list in your user.js becomes /api/user/load_user_list(from app.js) + /api/user/load_user_list.
So only when you hit /api/user/load_user_list/api/user/load_user_list, your router 2 will be called.
You can change your app.js code to
app.use('/api/user', require('./routes/user'));
and your routes/user.js to
//router 1
router.get('/', function (req, res, next) {
//do something here -
});
//router 2
router.get('/load_user_list', function (req, res, next) {
//do something here
});
//router 3
router.get('/get_user_detail', function (req, res, next) {
//do something here
});
Now, when you hit /api/user/load_user_list, it will match /api/user(app.js) + /load_user_list(routes/user.js) and the route which you wanted will be called.
I am working with Passport, and I need to pass multiple parameters through to from my controller to my router. Basically it only passes the first one.
I want to get
app.get('/auth/steam', controllers.auth.authenticate);
to result in
app.get('/auth/steam', passport.authenticate('steam'), function(req, res) { res.render('index') };);
Right now it only loads the 1st parameter.
My controller looks like this
exports.authenticate =
passport.authenticate('steam'),
function(req, res) {
res.render('index');
};
How would I do this?
EDIT: I want to only be able to call it with controllers.auth.authenticate, not in an array like: controllers.auth.authenticate[0]!
Warning NOT tested.
You can wrap all inside function
exports.authenticate = function(req, res, next) {
passport.authenticate('steam', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/auth/steam'); }
res.render("index");
});
}
Or you can use router and protect ALL verbs (get, post, etc)
var express = require('express');
var router = express.Router();
router.use(function (req, res, next) {
passport.authenticate('steam');
});
router.get('/', function(req, res, next) {
res.render("index");
});
module.exports = router;
And use router on the app
var ctrl = require("yourModuleName");
app.use('/auth/steam', ctrl); // app.use NOT app.get
Other alternative is to protect only the get
var express = require('express');
var router = express.Router();
router.get('/', passport.authenticate('steam'), function(req, res, next) {
res.render("index");
});
module.exports = router;
var ctrl = require("yourModuleName");
app.use('/auth/steam', ctrl); // app.use NOT app.get
See Express routing page
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.
I am following along with this article, which describes a good way to organize routes in express. I am encountering a problem though when i try to access the functions that i have exported from my main.js file. I get a 404 error when i curl "localhost/user/username"
//the routes section of my my app.js file
app.get('/', routes.index);
app.get('/user/:username', routes.getUser);
//my index.js file
require('./main');
require('./users');
exports.index = function(req, res) {
res.render('index', {title: 'Express'});
};
//my main.js file
exports.getUser = function(req, res){
console.log('this is getUser');
res.end();
};
----EDITED WITH MY SOLUTION----
Here is the solution that I went with, maybe someone will find it useful. I'm also open to hearing suggestions about whether or not this is going to cause me any problems in the future.
//-------The routes in my app.js file now look like this.
require('./routes/index')(app);
require('./routes/main')(app);
//-------In index.js i now have this
module.exports = function(app) {
app.get('/', function(req,res){
res.render('index', {title: 'Express'});
});
};
//-------My main.js now looks like this-------
module.exports = function(app){
app.get('/user/:username', function(req, res){
var crawlUser = require('../engine/crawlUser');
var username = req.params.username;
crawlUser(username);
res.end();
});
};
Globals are evil and should be avoided at all costs. Here is how I organize my routes without globals and without excessive boiler plate code.
// File Structure
/app.js
/routes
/--index.js
/--main.js
/--users.js
// app.js
var app = require('express');
/* Call Middleware here */
require('routes')(app);
app.listen(3000);
---------------------------------------------
// routes/index.js - This is where I store all my route definitions
// in a long list organized by comments. Allows you to only need to go to
// one place to edit route definitions.
module.exports = function(app) {
var main = require('./main');
app.get('/', main.get);
var users = require('./users');
app.get('/users/:param', users.get);
...
}
---------------------------------------------
// routes/main.js - Then in each submodule you define each function and attach
// to exports
exports.get = function(req, res, next){
// Do stuff here
})
I guess in the end its a matter of preference, but if you want your code to maintain agility and work with other modules you should avoid global variables. Even if Alex Young says its ok. =)
Here is the solution that I went with, maybe someone will find it useful.
//-------The routes in my app.js file now look like this.
require('./routes/index')(app);
require('./routes/main')(app);
//-------In index.js i now have this
module.exports = function(app) {
app.get('/', function(req,res){
res.render('index', {title: 'Express'});
});
};
//-------My main.js now looks like this-------
module.exports = function(app){
app.get('/user/:username', function(req, res){
var crawlUser = require('../engine/crawlUser');
var username = req.params.username;
crawlUser(username);
res.end();
});
};