How to chain middlewares using routers - node.js

I'm using express 4.0 and I'm having trouble to chain middlewares. I have 2 routers : a job router and a recruiter router.
Everything works fine at the moment ( I can use CRUDs on both of these routers) but I'd like the POST method for a job to call a method withing the recruiter router and I don't know to achieve this.
router/recruiter.js :
var express = require('express');
var router = express.Router();
/* GET recruiters listing. */
router.get('/', function(req, res, next) {
var recruiters = [];
//get recruiters
res.json(recruiters);
});
function(err, req, res, next) {
console.info("pseudo code for a function I'd like to call in the job.js file");
});
module.exports = router;
router/job.js :
var express = require('express');
var uuid = require('uuid4');
var router = express.Router();
var jobs = [];
/* GET job listing. */
router.get('/', function(req, res, next) {
jobs = [];
//get jobs
res.json(jobs);
});
/* add jobs . */
router.post('/', function(req, res, next) {
console.info('add job', req.body);
var body = req.body;
//I omit the parts where I check the req and save the object
//At the moment I do this but I'd like to call a method within the recruiter router before sending the json back to the client.
res.json({'jobs': []});
});
module.exports = router;
and here are the relevant parts in the app.js :
var job = require('./routes/job');
var recruiter = require('./routes/recruiter');
app.use('/job', job);
app.use('/recruiter', recruiter);

Turning my comment into an answer...
Express does not offer any special way to share code among multiple routes. If you want the code always executed before your route handler, you can, of course, use a common middleware function.
But, if your code sharing case is that you just want two or more routes to be able to execute some common code from within their route implementations, then this is really just a plain Javascript issue. You put the common code into a shared function and you call that function from two or more routes. In other words, you just share code among routes that same way you share code among any other Javascript functions. Express doesn't require anything special in this regard. Do it that way you always do it in Javascript.
It is fairly common that people coding in Express get caught up in the way you structure your code for Express and somehow forget that you can still use regular shared, common functions to share code (I've seen many people caught by this) - expecting their to be an "Express" way to share code. There isn't. Just do the normal Javascript method of sharing common code by creating a function with the common code in it and calling it from more than one place.

Related

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;

How to customise get method with different queries in Express.js with Node.js?

I'm currently building a REST API using Node.js with Express.js and I'm quite new to this technology. The following code shows the get method to a list of councils stored in MongoDB.
const { Council } = require('../mongoose-models/council');
const express = require('express');
const router = express.Router();
router.get('/', async (req, res) => {
const query = req.query;
const councilsList = await Council.find(query);
if (!councilsList) return res.status(404).send('No councils found.');
res.send(councilsList);
});
module.exports = router;
From my previous experience when developing REST API using java, I can customise different queries by implementing different methods with their own paths. For example:
#Path("findByCouncilName/{councilName}")
#Path("findCouncilsNotInMyArea/{longitude}/{latitude}")
And within each method, I can then write different logics. However, in Express.js, it seems that I have to implement all these different logics into one block. It seems not flexible and how can actually implement it? Furthermore, does the query must be same as the key name in MongoDB? What if I want to filter the results based on a specified index element in a nested array in a document?
For your routes:
#Path("findByCouncilName/{councilName}")
#Path("findCouncilsNotInMyArea/{longitude}/{latitude}")
If you are to implement them in express, you can split them into different blocks actually.
Instead of listening to '/' and try to handle everything inside, you can try this.
const express = require('express');
const router = express.Router();
router.get('/findByCouncilName/:councilName', async (req, res) => {
const councilName = req.params.councilName;
// Your logic goes here
res.send();
});
router.get('/findCouncilsNotInMyArea/:longitude/:latitude', async (req, res) => {
const longitude = req.params.longitude;
const latitude = req.params.latitude;
// Your logic goes here
res.send();
});
module.exports = router;
You can use it like lets say:
router.get('/:councilName', async (req, res) => {
Then use the parameter in the route with :
req.params.councilName
Express doc is your friend
https://expressjs.com/en/guide/routing.html
Here is everything you should know about express routing.
You can specify individual logic for every pat-method pair, and again, use general as needed.
You need to be aware of path order in which Express resolves them, eg. first path to match will will be executed.

NodeJS - Send result of a database query to a view or route before render

I'm making a webapp with NodeJS using Express JS, Socket IO and Handlebars, but i am pretty new to these technologies.
I'm struggling to find a way to pass the result from a query to my menu(a partial), mainly because Node is async, so by the time the result from my query returns, the page has already rendered, and the values never passed.
main.handlebars (Main layout):
(...)
{{> menu}}
(...)
{{{body}}}
router.js
var express = require('express');
var router = express.Router();
var index_controller = require('../controllers/index_Controller');
router.get('/', index_controller.index);
module.exports = router;
index_controller.js
exports.index = function(req, res) {
res.render('main_page_html');
};
This menu will appear in every page, and i want to show in this menu names from people online, that it's the result from the query.
I tried putting the code for the query inside the route function, and it works, but i would have to copy the same code to every route that i have, because as i said, this menu appears in all of them.
If i try to do a function outside the route function, async kicks in and no data is sent to the page.
There's definitely a better solution for this.
P.s.: Emit my data via socket to the client is one way, but i would like to do things server-side.
-Solution-
I did as Tolsee said, i created a middleware, so now i can call this middleware in every route that is needed. This is how i have done:
menu.js
exports.onlineUsers = function (req, res, next) {
// database query {
// res.locals.onlineUsers = queryResult;
// next();
// }
}
router.js
var menu_midd = require('../middleware/menu');
router.get('/', menu_midd.onlineUsers, index_controller.index);
module.exports = router;
index_controller.js
exports.index = function(req, res) {
res.render('main_page_html', {data: res.locals.onlineUsers});
};
Well, at least works for me. :)
There are two solutions to this problem. If you want to do it server side then you need to pass the menu data to handlebar/view. If you want it through all the pages then you can make a middleware function and employ it to all the router like below:
function middleware(req, res, next) {
// your code
// define your menu variable
// And assign it to res.locals
res.locals.menu = menu
next()
}
// let's employ it to all the routes
app.use(middleware)
// You can employ it to separate routes as well
myRouter.get('/sth', middleware, function(req, res) {
// your code
})
With that being said, If your menu is showing online users, you will need to use socket.io(even if you rendered it through server-side at the start) because you will need to update the online user list in real-time.

Node express routes as modules: where to put `require`s?

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.

what is proper mvc based directory structure for express js application with node js?

I am using express js for my web application. what should be the proper directory structure for seperating routing and business logic. also how to communicate in between them
I like to separate request handling, logic and response in three different folders.
./js/server/request
./js/server/response
./js/server/model
This is how I like to separate things, and I will be pleased to have a feedback, or other separation techniques.
The following example is based on express.
var express = require('express');
First, in your main server file, declare a router, so you can handle requests in separate files :
var routeName = require('./js/server/request/routerName');
var app = express();
app.use(routeName);
then you can handle every request you want in this file. In this file, do not forget to export the router at the end :
module.exports = router;
and to import the proper stuff :
var express = require('express');
Now you can handle your routes :
var router = express.Router();
router.get('/', function (err, req, res, next) {
// Put some route handling here
});
At this point, I extract data from the request that I need to "know what to do". Then, in the ./js/server/ directory, you can make two more folders : a response folder and a model folder.
Model folder : classes for "logic". Usually database communication, etc...
Response folder : takes something from the model class and sends it back to the client.
In your router, it could look like that :
router.get('/', function (err, req, res, next) {
var model = new Model();
var response = new Response(req, res);
model.doSomething(params, response);
});
Then the model does his work, and at the end, calls the response with accurate info to send !

Resources