Correct/Concisest way to nest routers with Express - javascript

I can set up two routes like this
index.js
var express = require('express');
var app = express();
var router = express.Router();
const PORT = 3001;
app.get('/', function(req, res){
res.send('hello app');
});
app.use('/routes', require('./routes'));
app.listen(PORT, function(){
console.log('listening on port:', PORT);
});
./routes/index.js
var express = require('express');
var app = express();
var router = express.Router();
router.use('/sub1', require('./sub1'));
router.use('/sub2', require('./sub2'));
module.exports = router;
./routes/sub1.js
var express = require('express');
var app = express();
var subOneRouter = express.Router();
subOneRouter.get('/', function(req, res){
res.json({route: 'sub1-base'});
});
subOneRouter.get('/:id', function(req, res){
res.json({'route': 'sub1-base', 'id': req.params.id});
});
module.exports = subOneRouter;
For brevity ./routes/sub2.js looks exactly the same, but its variables are named subTwo
What is the shortest way to nest sub2 under sub1? Within index.js I have tried
var subOne = router.use('/sub1', require('./sub1'));
subOne.use('/sub2', require('./sub2'));
But that didn't work at all. Within index.js
router.use('/sub1/:id/sub2', require('./sub2'));
//localhost:3000/sub1/123/sub2/456 => { "route": "sub2-base","id":"456"}
Does work, but it seems it could get verbose and difficult to maintain if the structure got much longer. What's the best way to do this? Is there a shorter way to nest these?

Your code in index.js makes it difficult to understand what you want. So far I understand you want a route like /sub1/:id/sub2 but more easy to write and maintain and inside index.js.
So yes you can do it and it is quite simple.
You just need to require sub1 and sub2 and use sub2 in sub1, then you can mount sub1 on the router.
Ex:
var sub1= require('./sub1');
var sub2 = require('./sub2');
sub1.use(sub2);
router.use('/sub1:id', sub1);
So your index.js becomes,
var express = require('express');
var app = express();
var router = express.Router();
const PORT = 3001;
app.get('/', function(req, res){
res.send('hello app');
});
var sub1= require('./sub1');
var sub2 = require('./sub2');
sub1.use(sub2);
router.use('/sub1:id', sub1);
app.listen(PORT, function(){
console.log('listening on port:', PORT);
});
This wouldn't be very difficult to maintain. Let me know if this isn't what you are looking for.

So you have
/routes/sub1
/routes/sub1/:id
/routes/sub2
/routes/sub2/:id
if i understand correctly, you want these routes:
/routes/sub1/sub2
/routes/sub1/sub2/:id
/routes/sub1/:id/sub2
/routes/sub1/:id/sub2/:id
So your solution can be something similar to this:
var express = require('express'),
app = express();
routerA = express.Router(),
routerB = require('./sub2');
routerA.get('/', function(req, res) { console.log('sub1 base') });
//The next 2 mounts order matters, cause they can overlap each other
routerA.use('/sub2', routerB); //routes/sub1/sub2 + routes/sub1/sub2/:id
routerA.get('/:id', function(req, res) { console.log('sub1 id:' + req.params.id) });
routerA.use('/:id/sub2', routerB); //routes/sub1/:id/sub2 + routes/sub1/:id/sub2/:id
app.use('/routes', routerA);
app.listen(3000);
It is up to you to decide that will there be a sub1-id called 'sub2'
Cause any request to the resource with id 'sub2' will be taken a way by routerB

The way I would recommend to write your node.js routes would be to have a routes.js file, which will contain all your routes.
You can then put, in your routes.js file your route for sub1, like so:
const router = require('express').Router();
const sub1 = require('./sub1');
router.use('/sub1', sub1);
module.exports = router;
I would then, in sub1.js, put your sub2 route, like so:
const subOneRouter = require('express').Router();
subOneRouter.get('/', function(req, res){
res.json({route: 'sub1-base'});
});
subOneRouter.get('/:id', function(req, res){
res.json({'route': 'sub1-base', 'id': req.params.id});
});
const sub2 = require('./sub2');
subOneRouter.use('/:id/sub2);
module.exports = subOneRouter;
This will make all your sub1 routes have the prefix of '/sub1' and all your sub2 prefixes, inside sub1, have a prefix of '/:id/sub2'. You can then feel free to put whatever routes inside sub2 that you wish.
Using this setup means that if you ever want to change the prefix for sub1, or sub2, you just change it in one place.

Related

Express route not being recognized. Responds with 'Cannot GET'

app.js
var express = require("express");
var app = express();
var path = require('path');
var db = require('./db');
var bodyParser = require('body-parser');
app.listen(80);
app.set('view engine', 'jade');
app.set('views', "./views");
// app.get('/', _GetMainPage);
// app.get('/sites', _GetSites);
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.urlencoded({ extended: true })); // Support encoded bodies
app.use(bodyParser.json()); // Support json encoded bodies
app.use(require('./controllers'));
./controllers/index.js
var express = require('express');
var router = express.Router();
router.use('/', require('./sites'));
router.use('/site', require('./site'));
module.exports = router;
./controllers/sites.js
var express = require('express');
var router = express.Router();
var site = require('../models/site');
router.get('/', function(req, res) {
site.getAll(function(err, rows){
if(err) {
res.send(err);
return;
}
res.render('sites', { sites : rows });
});
});
./controllers/site.js
var express = require('express');
var router = express.Router();
var site = require('../models/site');
router.get('/site', function(req, res) {
// console.log("get /site received. req.body: " + req.body);
res.render('site', {
site: {
name : req.params.name
}
});
});
module.exports = router;
When I request localhost/site I get a response saying:
Cannot GET /site
localhost/ works perfectly
I have been looking at this for a while and can't find the problem yet. If there is anything I can add, let me know. Thanks.
Thank you to the person that commented with the answer:
What happens if you navigate to /site/site? Your site.js route is relative to the route you provided in use. So it should be router.get('/' ... not router.get('/site' ...
The ./controllers/site route is already being routed to /site. On top of this I was calling router.get('/site', ...). This means it was actually routing to /site/site.
The solution is to just use router.get('/', ...) in the site.js file instead.
This really helped me, thank you.
Basically, the root path in the sub-app is defined in your core app where you mount it via the app.use() method.
the best example I can find from app.mountpath docs is here:
https://expressjs.com/en/4x/api.html#express.router
The app.mountpath property contains one or more path patterns on which a sub-app was mounted.
var express = require('express');
var app = express(); // the main app
var admin = express(); // the sub app
admin.get('/', function (req, res) {
console.log(admin.mountpath); // /admin
res.send('Admin Homepage');
});
app.use('/admin', admin); // mount the sub app
It is similar to the baseUrl property of the req object, except
req.baseUrl returns the matched URL path, instead of the matched
patterns.
If a sub-app is mounted on multiple path patterns, app.mountpath
returns the list of patterns it is mounted on, as shown in the
following example.
var admin = express();
admin.get('/', function (req, res) {
console.log(admin.mountpath); // [ '/adm*n', '/manager' ]
res.send('Admin Homepage');
});
var secret = express();
secret.get('/', function (req, res) {
console.log(secret.mountpath); // /secr*t
res.send('Admin Secret');
});
admin.use('/secr*t', secret); // load the 'secret' router on '/secr*t', on the 'admin' sub app
app.use(['/adm*n', '/manager'], admin); // load the 'admin' router on '/adm*n' and '/manager', on the parent app

How to split Node.js files in several files

I have this code:
var express = require("express");
var app = express();
var path = require("path");
app.use(express.static(__dirname + '/public'));
app.get('/',function(req,res){
res.sendFile(path.join(__dirname+'/views/index.html'));
res.set('Access-Control-Allow-Origin', '*');
}).listen(3000);
console.log("Running at Port 3000");
app.get('/test', function(req, res) {
res.json(200, {'test': 'it works!'})
})
I will have many services (like the test one), and I don't want to have them all on the same file.
I read in another question in Stack Overflow, that I can require other files like this: var express = require("./model/services.js"); And in that file write all the services, but it's throwing app is not defined when I start Node.
How can I separate codes?
You can define your routes in different files say test-routes.js like this:
module.exports = function (app) {
app.get('/test', function(req, res) {
res.json(200, {'test': 'it works!'})
})
}
Now in your main file say server.js you can import your route file like this:
var express = require("express");
var app = express();
var path = require("path");
app.use(express.static(__dirname + '/public'));
app.get('/',function(req,res){
res.sendFile(path.join(__dirname+'/views/index.html'));
res.set('Access-Control-Allow-Origin', '*');
}).listen(3000);
console.log("Running at Port 3000");
// import your routes
require('./test-routes.js')(app);
your test.js should look something like:
var express = require('express');
var router = express.Router();
router.get('/test', function (req, res) {
res.json(200, {'test': 'it works!'});
});
module.exports = router;
and the app.js (assuming other is some other route defined similarly to test.js):
var test = require("./routes/test.js");
var other = require("./routes/other.js");
...
//all your code for creating app
...
app.use('/test', test);
app.use('/other', other);

Using a router with Express and Node.js

I'm new to Node.js. I've pulled some code from examples, but somehow I've broken something :).
At this time, in my app.js file, I have a line that I think wires up Express with Node.js. That line looks like this:
app.js
var routes = require('./routes/index');
// ...
app.get('/', routes.router);
Then, in ./routes/index.js I have the following:
routes/index.js
var express = require('express');
var router = express.Router();
/* GET home page */
router.get('/', function(req, res) {
res.send('respond with a resource');
});
module.exports = router;
When I run this, I get the following error:
Error: Route.get() requires callback functions but got a [object Undefined]
at Route.(anonymous function) [as get]
I don't understand. What am I doing wrong?
Thanks!
app.js
var routes = require('./routes/index');
//var routes = require('./routes') --> this works
// ...
app.use('/', routes); //Using the router instance as a middleware , relative to '/'
routes/index.js
var express = require('express');
var router = express.Router(); // new instance of Router
/* GET home page */
router.get('/', function(req, res) {
res.send('respond with a resource');
});
module.exports = router; // You export the intance
UPDATE
if you want more than 1 route file
app.js
var routes = require('./routes')
app
.use("/user",routes.user)
.use("/other",routes.other)
routes/index.js
module.exports = {
user : require(./user),
other : require(./other)
}
routes/user.js
var router = require("express").Router()
router.get("/",function (req,res){
// GET /user
})
.post("/",function (req,res){
//POST /user
})
module.exports = router;
routes/other.js
var router = require("express").Router()
router.get("/",function (req,res){
// GET /other
})
.post("/",function (req,res){
//POST /other
})
module.exports = router;
An example of a basic server.js:
var express = require('express');
var app = express();
app.get('/', function(req, res) {
res.sendfile(__dirname + '/client/views/index.html');
});
app.listen(3000, function() {
console.log('Server running on localhost:3000');
});
Comparing, I believe you need to listen to the port. Also I think your res.send should be sending an actual file.
More documentation on Nodejs here:
https://nodejs.org/api/
Also found a related questions on SO:
Node Route.get() requires callback function but got a [object undefined]

Node JS App Better design and seperation

I've created a node application with express. I try to separate the following layers which will give me the ability to test the application with unit testing...
The problem is that I don't know how to call to the router.js file which will stops in the post/get/delete application.
The server.js file looks as follows
http = require('http'),
app = require('./app')(),
http.createServer(app).listen(app.get('port'), function (err) {
console.log('Express server listening on port ' + app.get('port'));
});
This is the app.js file
var express = require('express'),
logger = require('morgan'),
bodyParser = require('body-parser'),
routesApp = require('./ro/route');
module.exports = function () {
var app = express();
app.set('port', process.env.PORT || 3005);
app.use(logger('dev'));
app.use(function (req, res, next) {
res.set('APP', 'User app');
next();
});
app.use(bodyParser.json());
app.use(routesApp);
return app;
};
This is the router.js, which will route the call to other module according to the http type like post/delete/get etc...
var handleGet = require('../controller/handleGet');
var handlePost = require('../controller/handlePost');
var express = require('express');
module.exports = function (app) {
var appRoute = express.Router();
app.use(appRoute);
appRoute.route('*')
.post(function (req, res) {
handlePost(req, res);
})
.get(function (req, res) {
handleGet(req, res)
})
Currently I've two questions:
How to make it work since when in debug It dump in
app.use(appRoute); on the router.js file?
The error is TypeError: undefined is not a function
Is it good way to structure the node app like in my post? I want to seperate all this layers like SOC, I'm fairly new to node and express and I try to build it to be modular and testable...
How to make it work since when in debug It dump in app.use(appRoute); on the router.js file? The error is TypeError: undefined is not a function
This fails because you don't pass app into the module when you require it in app.js, you would need to do something like
app.use(routesApp(app)); // <- this hurts my eyes :(
Is it good way to structure the node app like in my post?I want to sperate all this leyrs like SOC,I fairly new to node and express and I try to build it to be modular and testable...
Your definitely on the right track, keeping things separated is generally always a good idea. Testing is definitely one of the big pluses but it also helps with other things like maintainability & debugging.
Personally, I would make use of the bin directory for any start up script configuration
bin/www
var app = require('./app');
app.set('port', process.env.PORT || 3005);
var server = app.listen(app.get('port'), function() {
console.log('Express server listening on port ' + app.get('port'));
});
This will help decouple your express app from all the environment setup. This should keep your app.js clean and only contain app-related config
app.js
var express = require('express')
, app = express()
, logger = require('morgan')
, bodyParser = require('body-parser')
, routes = require('./routes.js');
app.use(logger('dev'));
app.use(function (req, res, next) {
res.set('APP', 'User app');
next();
});
app.use(bodyParser.json());
app.use('/', routes);
...
module.exports = app;
Then finally, your routes.js should do nothing but handle your URLs
routes.js
var express = require('express')
, router = express.Router()
, handleGet = require('../controller/handleGet')
, handlePost = require('../controller/handlePost');
router.get('/', handleGet);
router.post('/', handlePost);
...
module.exports = router;

request.body undefined after using express bodyParser

Edit: i fixed it by using:
app.configure(function(){
app.use(express.bodyParser());
});
Original post:
I'm trying to figure out how to handle a post with node and express and i'm completely stuck.
After some reading i noticed people saying i should use 'middleware' whatever that means and create a line app.use(express.bodyParser());. I assumed that after adding that i would have a req.body available in my post method. This isn't the case however. It console.log's into a undefined.
I think I don't know how to properly set this up, so here goes nothing:
var express = require('express')
, routes = require('./routes')
, user = require('./routes/user')
, http = require('http')
, path = require('path')
, UserProvider = require('./userprovider').UserProvider,
qs = require('querystring');
var userProvider = new UserProvider('localhost', 27017);
var app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server);
server.listen(8080);
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});
app.get('/new_game', function (req, res) {
res.sendfile(__dirname + '/new_game.html');
});
app.post('/new_game', function(req, res) {
var codeToUse = Math.random()*10000;
codeToUse = Math.round(codeToUse);
console.log(req.body);
});
app.use(express.bodyParser());
app.listen(3000);
Though you've said now your code works, but i won't suggest you to use bodyParser in the options of
app.configure()
What it does is that, if you use it as you have done, any file can be send into your system for all post requests. It's better if you use
express.json()
and
express.urlencoded()
in the options of
app.configure(),
and when you expect a file use bodyParser in the respective post route like this
app.post('/upload', express.bodyParser(), function(req, res){//do something with req.files})

Categories

Resources