Node express routes as modules: where to put `require`s? - node.js

Node Express's Routing guide gives the following example for creating routes as modules:
/birds.js:
var express = require('express')
var router = express.Router()
// middleware that is specific to this router
router.use(function timeLog (req, res, next) {
console.log('Time: ', Date.now())
next()
})
// define the home page route
router.get('/', function (req, res) {
res.send('Birds home page')
})
// define the about route
router.get('/about', function (req, res) {
res.send('About birds')
})
module.exports = router
/app.js:
var birds = require('./birds')
// ...
app.use('/birds', birds)
I want to know why they put the first two lines of birds.js there instead of in app.js.
Firstly, app.js calls a method of app. How is app supposed to be defined within app.js? I take it they (oddly) neglected to include that necessary code for the sake of the tutorial.
Secondly, say I wanted a second route as a module, for dogs as well as birds, in a file called dogs.js. Could it look identical to birds.js WRT the first two lines? AFAIK that would result in two instances of express. (Or three if it's needed in app.js as well?!)

The example is not complete. The whole app setup is left out (i asume because it is explained further up in the docs anyway and replaced with // ...). In your app.js you need at least:
var express = require('express');
var app = express();
The first 2 lines in bird.js have nothing to do with the two (missing) lines in app.js. You need them to create a router.
And regarding your last question: Yes, you would create another router exactly like the bird router. A router is not an express app/instance and it's totally fine to have multiple of them in you app.

Related

Confusion with app.use with Router() in express

I was learning Express in Node.js and came across Router() allowing us to modularize our routes. But than I found this code:
// we'll create our routes here
// get an instance of router
var router = express.Router();
...
// route with parameters (http://localhost:8080/hello/:name)
router.get('/hello/:name', function(req, res) {
res.send('hello ' + req.params.name + '!');
});
// apply the routes to our application
app.use('/', router);
What confused me is that why we need to use app.use('/', router); to apply the routes. That is, what if we use app.get('/', router);
I am giving you a simple code example to make you understand the use of express.Router(). Yes you are right that it helps in modularization. Actually it makes our main app.js file clean from all the routes. We just put all those routes based on their purposes in different files and require them when needed.
so suppose I have two files app.js and register.js
// app.js file's code goes here
let express = require("express")
let app = express()
let register = require("./routes/register")
app.use(register) // This will tell your app to import all those routes which are in register
// register.js file's code goes here
let express = require("express")
let router = express.Router()
router.get("/register", callback_function);
router.post("/register", callback_function);
module.exports = router;
So basically what I am trying to show is your register.js can contain all types of HTTP requests(GET, POST, PUT,...) and when we use app.use(register) this will handle all those routes. app.get("route_path", callback_function) is only for handling get requests to that path.
Router is just a middleware of it's own. If you use app.get('/', router) you will use the router just for GET requests. Using use channels all requests there.
When app.use is used then it handled all the HTTP methods, but when app.get is used it takes just GET method.
Added advantage to app.use is that route will match any path that follows its path immediately with a /.
For example:
app.use('/v1', ...)
will match /users, /users/accounts, /users/accounts/account-id, and so on.

Express - can't import routes

I have a following structure,
// app.js
const express = require("express");
const app = express();
app.get("/", require("./routes/index"));
app.get("/users", require("./routes/users"));
app.listen(3000);
// /routes/index.js
const express = require("express");
const router = express.Router();
router.get("/", (req, res) => res.send("index"));
module.exports = router;
// /routes/users.js
const express = require("express");
const router = express.Router();
router.get("/login", (req, res) => res.send("login"));
router.get("/register", (req, res) => res.send("register"));
module.exports = router;
If I use app.use(...) inside app.js then the routes work correctly but I want to use app.get since I want to block any other accessible method.
Right now the index route works fine but other routes does not work.
Working sandbox: https://codesandbox.io/s/cocky-cartwright-h82d2?fontsize=14&hidenavigation=1&theme=dark
The correct way to achieve what you're trying is to utilize the 'use' function.
You said you don't want to use the app.use because you want to block any other accessible method, but utilizing the 'use' function won't allow anything that wasn't declared on your routers, so you don't need to worry about that.
What app.use does is register a middleware function to your app on the specified route. So, the code below:
app.use("/users", require("./routes/users"));
Will make that every request that match the pattern '/users' will utilize the function you provided (in this case, the router inside the users.js file).
So, if someone sends a POST request to /users/register, per example, he will get a 404, because you never created a route to handle a post on that path.
And just to make it clear why the app.get don't work the way you did, let me give a brief explanation.
When you use the app.get function, it'll be expected that the request path matches exactly the one you provided. So, when you do the following:
app.get("/users", require("./routes/users"));
The server will be expecting a request that matches /users exactly. So, something like /users/example will not trigger the callback function.
The thing is, the function to handle the request in the code above is another router:
router.get("/login", (req, res) => res.send("login"));
router.get("/register", (req, res) => res.send("register"));
So, the router will expect that the request path matches /users/login or /users/register exactly. But, if a request path matches /users/register, it will not match /users, so nothing will be called.
The reason why your '/' path works right now is because your router is expecting the same path you used on the app.js file. So, a request to '/' will match both patterns.
you can't use router.get and pass a router
router.get expect to get a method (function).
you should use app.use or use controllers like that
// /controllers/index.js
exports.renderIndex = (req, res) => {
res.send("index")
}
and in app.js
// app.js
const express = require("express");
const app = express();
const indexController = require("./controllers/index");
app.get("/", indexController.renderIndex);
app.listen(3000);

catch plural route in expressjs

in expressjs, I use routing like below;
app.use('/game', require("./routes/game"));
in the file /routes/game.js
const express = require('express');
var router = express.Router();
router.get("s", function (req, res, next) {
res.send("GAME LIST");
})
router.get("/:gameurl", function (req, res, next) {
res.send(`GAME: ${req.params.gameurl}`);
})
module.exports = router;
I'd like to catch both /games and /game/wow
How can I manage to handle both routes separately?
If you want both /games and /game to go to your router, but not any other top level paths, then there are a number of ways to specify it. You can see them described here in the doc. For example, you could use a regex or pass multiple strings. In this case, I'll show you the multiple strings:
app.use(["/game", "/games"], require("./routes/game"));
For the route path, you can pass a single string, a path pattern (an Express subset of regex), a regex, or an array that contains any combination of these.
If you want to be able to tell the difference between /game and /games in your router, then you will have to examine req.originalUrl to see which one caused it to go to your router which seems to me to kind of defeat part of the purpose of routing in the first place.
Thus, sending two separate top level paths to the same router and routing them differently inside the router is not a design that works well with Express. Personally, I'd either change my path design so this doesn't happen or use two routers as that fits better with the router mechanics.
you can do something like this
app.use('/', require("./routes/game"));
in the file /routes/game.js
const express = require('express');
var router = express.Router();
const base = '/game';
router.get(`${base}s`, function (req, res, next) {
res.send("GAME LIST");
})
router.get(`${base}/:gameurl`, function (req, res, next) {
res.send(`GAME: ${req.params.gameurl}`);
})
module.exports = router;

Express: how can I get the app from the router?

I know I can get the Express app from inside an individual route with:
req.app
However I need to start a single instance of a module inside routes/index.js, i.e.:
var myModule = require('my-module')(propertyOfApp)
How can I get the express app from the router?
It really depends on your own implementation, but what I suggested in the comments should be working:
// index.js
module.exports = function(app) {
// can use app here
// somehow create your router and do the magic, configure it as you wish
router.get('/path', function (req, res, next) {});
return router;
}
// app.js
// actually call the function that is returned by require,
// and when executed, the function will return your configured router
app.use(require('./index')(app));
p.s.
Of course this is just a sample - you can configure your router with path, and all kind of properties you wish. Cheers! :)

Express Routes in Parse Cloud Code Module

I am using parse.com cloud code with express to setup my routes. I have done this in the past with node, and I have my routes in separate files. So, in node I do
app.js
express = require("express");
app = exports.app = express();
require("./routes/js/account");
account.js
app = module.parent.exports.app;
app.get("/api/account/twitter", passport.authenticate("twitter"));
All the examples on parses site https://parse.com/docs/cloud_code_guide#webapp show this being done as follows.
app.js
var express = require('express');
var app = express();
app.get('/hello', function(req, res) {
res.render('hello', { message: 'Congrats, you just set up your app!' });
});
So, I would like to change the bottom to include a routes folder with separate routes files, but am not sure how to do this in parse.
I know this post is a little old, but I just wanted to post a solution for anyone still looking to get this to work.
What you need to do, is create your route file, I keep them in 'routes' forlder, for example <my_app_dir>/cloud/routes/user.js
Inside user.js you will have something that looks like this:
module.exports = function(app) {
app.get("/users/login", function(req, res) {
.. do your custom logic here ..
});
app.get("/users/logout", function(req, res) {
.. do your custom logic here ..
});
}
Then, in app.js you just include your file, but remember that you need to append cloud to the path, and pass the reference to your app instance:
require('cloud/routes/user')(app);
Also, remember that express evaluates routes in order, so you should take that into consideration when importing several route files.
I'm using a different method, have the routes in app.js, but you can probably include them in file if you prefer. Take a look at the example app,
anyblog on github
The way it works:
Set up a controller:
// Controller code in separate files.
var postsController = require('cloud/controllers/posts.js');
Add the controller route
// Show all posts on homepage
app.get('/', postsController.index);
// RESTful routes for the blog post object.
app.get('/posts', postsController.index);
app.get('/posts/new', postsController.new);
And then in posts.js, you can use exports, ex.
var Post = Parse.Object.extend('Post');
// Display all posts.
exports.index = function(req, res) {
var query = new Parse.Query(Post);
query.descending('createdAt');
query.find().then(function(results) {
res.render('posts/index', {
posts: results
});
},
function() {
res.send(500, 'Failed loading posts');
});
};
// Display a form for creating a new post.
exports.new = function(req, res) {
res.render('posts/new', {});
};
Pass the app reference to the post controller, and add the routes from there

Resources