Pass object to routes file used as middleware in nodejs - node.js

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" ) {
.....
}

Related

Express router going inside the same route

I have setup 3 different routes which have their own subroutes
router.use('/items', handleItems(app, router));
router.use('/price', handlePrice(app, router));
router.use('/documents', handleDocuments(app, router));
But when i call http://localhost:3000/api/documents/, it is throwing error as if it is calling functions inside /items routes. How can this happen?
After some more debugging i noticed that call is going inside handleItems handler to the /:id route
function handleItems(app, router) {
router.post('/create-one', itemController.createItem);
router.get('/:id', itemController.getItem);
return router;
}
Please refer to express.Router documentation.
In your example you have:
function handleItems(app, router) {
router.post('/create-one', itemController.createItem);
router.get('/:id', itemController.getItem);
return router;
}
The first parameter is not used, and the second parameter is your router object, so basically, you are registering your routes in the same object and you will end up with something like
/items
/create-one
/:id
/price
What you are looking for is something like:
const { Router } = require("express");
const express = require("express");
const app = express();
app.use("/items", handleItems(Router()));
app.use("/documents", handleDocuments(Router()));
function handleItems(r) {
r.get("/tada", (req, res) => res.json({ msg: `Tada` }));
r.get("/:name", (req, res) => res.json({ msg: `Hello ${req.params.name}` }));
return r;
}
function handleDocuments(r) {
r.get("/", (req, res) => res.json({ msg: "Bonjour" }));
return r;
}
app.listen(3000, () => console.log("🚀 Listening"));
In the example above, I registered a "new router" under each path (/items and /documents)
then, I registered each of these at their root paths:
app.use("/items", handleItems(Router()));
With:
function handleItems(r) {
router.post('/create-one', itemController.createItem);
router.get('/:id', itemController.getItem);
return r;
}
I don't know which architecture or what pattern you are using in your application, but if you are not using any you may want to use the following:
Create a routes.js file for each of items, price and documents. For example:
// items.routes.js
const { Router } = require("express");
const router = Router();
router.post('/create-one', itemController.createItem);
router.get('/:id', itemController.getItem);
module.exports = router;
Then in the app.js
const express = require("express");
const app = express();
// load routers
const itemsRoutes = require('./path/to/items.routes.js');
const documentsRoutes = require('./path/to/documents.routes.js');
app.use("/items", itemsRoutes);
app.use("/documents", documentsRoutes);
app.listen(3000, () => console.log("🚀 Listening"));
This might be a cleaner and more predictable pattern to work with in many cases

Express middlware not called on sub paths

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
// ...
});

Grouping routes in Express

We can group our routes like this in Laravel:
Route::group("admin", ["middleware" => ["isAdmin"]], function () {
Route::get("/", "AdminController#index");
Route::post("/post", ["middleware" => "csrf", "uses" => "AdminController#index");
});
Basically, all the routes defined in admin group gets the isAdmin middleware and group name automatically. For example, post endpoint listens to admin/post not /post
Is there any way to do the same thing with Express? It would be awesome because my Laravel routes used to be so clean, whereas my Express routes are a bit messy/duplicated.
This is my routes.js on Express at the moment.
app.get("/admin", [passportConfig.isAuthenticated, passportConfig.isAdmin], AdminController.index);
app.post("/admin", [passportConfig.isAuthenticated, passportConfig.isAdmin], AdminController.postIndex);
Thank you.
Since express 4 you can define and compose routers
const app = require('express');
const adminRouter = app.Router();
adminRouter.use(isAdmin);
adminRouter.get('/', admin.index); /* will resolve to /admin */
adminRouter.post('/post', csrf, admin.index); /* will resolve to /admin/post */
app.use('/admin', adminRouter);
Hope that helps!
Just use before of every group you want to do:
app.use('/admin', AdminMiddleware);
app.get('/admin/route1', ...
app.get('/admin/route2', ...
app.use('/user', UserMiddleware);
app.get('/user/route1', ...
app.get('/user/route2', ...
require('express-group-routes');
var app = require('express');
app.group("/api/v1", (router) => {
router.get("/login", loginController.store); // /api/v1/login
});
In case you don't want to add a prefix but still need to group certain routes you can leave the first parameter and go straight for the function:
require('express-group-routes');
var app = require('express');
app.group((router) => {
router.use(middleware);
});
You can use app.use() - https://expressjs.com/en/guide/using-middleware.html#middleware.application
app.use("/admin",[passportConfig.isAuthenticated, passportConfig.isAdmin],AdminController)
// AdminController:
var express = require('express');
var router = express.Router();
router.get('/', AdminController.index);
// etc...
module.exports = router
https://expressjs.com/en/guide/routing.html#express-router
I found some better solution you can follow this method it's working good
Route file route/user.js
var express = require('express')
var router = express.Router()
const authMiddleware = require('../middleware/auth')
express.application.prefix = express.Router.prefix = function(path, middleware, configure) {
configure(router);
this.use(path, middleware, router);
return router;
}
router.prefix('/user', authMiddleware, async function (user) {
user.route('/details').get(function(req, res) {
res.status(201).send('Hello this is my personal details')
}); //also you can use controller method if you have any
});
module.exports = router //make sure you have to import/use this route in main/server js
You can use an npm module
Express group route
here is a code example from npm official module
var app = require('express');
require('express-group-routes');
app.group("/api/v1", (router) => {
router.get("/login", loginController.store); // /api/v1/login
});
Create the group method
export const group = ((callback: (router: Router) => void) => {
const router = express.Router();
callback(router);
return router;
});
Use the group method
import { group } from './relative/path/to/group/method'
const apiRouter = express.Router();
apiRouter.use('/foo', group((router) => {
router.get('/bar', (req, res) => {
res.send('Hello World');
});
}));
This will create a new GET route to "/foo/bar"
I just wrote this module to solve your problem: https://github.com/benjamin658/express-inject-middleware
You can group your middlewares as an array and pass it to the express-inject-middleware...
For example:
import express from 'express';
import { injectMiddleware } from 'express-inject-middleware';
const app = express();
const authMiddleware = (req, res, next) => {
// some auth logic...
};
const fooMiddleware = (req, res, next) => {
// some foo logic
}
const barMiddleware = (req, res, next) => {
// some bar logic
}
app.use(injectMiddleware(
[
authMiddleware,
fooMiddleware,
],
[
// Passing the app.[METHOD] as the parameter.
app.get('/secrets', (req, res, next) => res.send('secrets'));
// Mount barMiddleware itself
app.post('/secrets', barMiddleware, (req, res, next) => res.send('ok'));
],
));
and this is the result:
app.get('/secrets', authMiddleware, fooMiddleware, (req, res, next) => res.send('secrets'));
app.post('/secrets', authMiddleware, fooMiddleware, barMiddleware, (req, res, next) => res.send('ok'));
in express 4 to grouping your routes, you should create some changes :
seperate route files in multiple files like admin and front
pass the router to the divided route files in routes/index.js file
const express = require('express')
require('express-group-routes');
const router = express.Router()
require('./admin/web.router')(router)
require('./front/web.router')(router)
module.exports = router
in each files we can use groups, middlewares and prefixes, like below
const adminPrefix = 'admin'
module.exports = function (router) {
router.use(`\${adminPrefix}`, [ adminMiddleware ]);
router.group(`/${adminPrefix}`, (router) => {
router.get('/', home.index)
});
}

How to export all routes in Express?

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)

How to include route handlers in multiple files in Express? [duplicate]

This question already has answers here:
How to separate routes on Node.js and Express 4?
(9 answers)
Closed 1 year ago.
In my NodeJS express application I have app.js that has a few common routes. Then in a wf.js file I would like to define a few more routes.
How can I get app.js to recognize other route handlers defined in wf.js file?
A simple require does not seem to work.
If you want to put the routes in a separate file, for example routes.js, you can create the routes.js file in this way:
module.exports = function(app){
app.get('/login', function(req, res){
res.render('login', {
title: 'Express Login'
});
});
//other routes..
}
And then you can require it from app.js passing the app object in this way:
require('./routes')(app);
Have a look at these examples: https://github.com/visionmedia/express/tree/master/examples/route-separation
In Express 4.x you can get an instance of the router object and import another file that contains more routes. You can even do this recursively so your routes import other routes allowing you to create easy-to-maintain URL paths.
For example, if I have a separate route file for my /tests endpoint already and want to add a new set of routes for /tests/automated I may want to break these /automated routes out into a another file to keep my /test file small and easy to manage. It also lets you logically group routes together by URL path which can be really convenient.
Contents of ./app.js:
var express = require('express'),
app = express();
var testRoutes = require('./routes/tests');
// Import my test routes into the path '/test'
app.use('/tests', testRoutes);
Contents of ./routes/tests.js:
var express = require('express'),
router = express.Router();
var automatedRoutes = require('./testRoutes/automated');
router
// Add a binding to handle '/tests'
.get('/', function(){
// render the /tests view
})
// Import my automated routes into the path '/tests/automated'
// This works because we're already within the '/tests' route
// so we're simply appending more routes to the '/tests' endpoint
.use('/automated', automatedRoutes);
module.exports = router;
Contents of ./routes/testRoutes/automated.js:
var express = require('express'),
router = express.Router();
router
// Add a binding for '/tests/automated/'
.get('/', function(){
// render the /tests/automated view
})
module.exports = router;
Building on #ShadowCloud 's example I was able to dynamically include all routes in a sub directory.
routes/index.js
var fs = require('fs');
module.exports = function(app){
fs.readdirSync(__dirname).forEach(function(file) {
if (file == "index.js") return;
var name = file.substr(0, file.indexOf('.'));
require('./' + name)(app);
});
}
Then placing route files in the routes directory like so:
routes/test1.js
module.exports = function(app){
app.get('/test1/', function(req, res){
//...
});
//other routes..
}
Repeating that for as many times as I needed and then finally in app.js placing
require('./routes')(app);
If you're using express-4.x with TypeScript and ES6, this would be the best template to use:
src/api/login.ts
import express, { Router, Request, Response } from "express";
const router: Router = express.Router();
// POST /user/signin
router.post('/signin', async (req: Request, res: Response) => {
try {
res.send('OK');
} catch (e) {
res.status(500).send(e.toString());
}
});
export default router;
src/app.ts
import express, { Request, Response } from "express";
import compression from "compression"; // compresses requests
import expressValidator from "express-validator";
import bodyParser from "body-parser";
import login from './api/login';
const app = express();
app.use(compression());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressValidator());
app.get('/public/hc', (req: Request, res: Response) => {
res.send('OK');
});
app.use('/user', login);
app.listen(8080, () => {
console.log("Press CTRL-C to stop\n");
});
Much cleaner than using var and module.exports.
Full recursive routing of all .js files inside /routes folder, put this in app.js.
// Initialize ALL routes including subfolders
var fs = require('fs');
var path = require('path');
function recursiveRoutes(folderName) {
fs.readdirSync(folderName).forEach(function(file) {
var fullName = path.join(folderName, file);
var stat = fs.lstatSync(fullName);
if (stat.isDirectory()) {
recursiveRoutes(fullName);
} else if (file.toLowerCase().indexOf('.js')) {
require('./' + fullName)(app);
console.log("require('" + fullName + "')");
}
});
}
recursiveRoutes('routes'); // Initialize it
in /routes you put whatevername.js and initialize your routes like this:
module.exports = function(app) {
app.get('/', function(req, res) {
res.render('index', { title: 'index' });
});
app.get('/contactus', function(req, res) {
res.render('contactus', { title: 'contactus' });
});
}
And build yet more on the previous answer, this version of routes/index.js will ignore any files not ending in .js (and itself)
var fs = require('fs');
module.exports = function(app) {
fs.readdirSync(__dirname).forEach(function(file) {
if (file === "index.js" || file.substr(file.lastIndexOf('.') + 1) !== 'js')
return;
var name = file.substr(0, file.indexOf('.'));
require('./' + name)(app);
});
}
I am trying to update this answer with "express": "^4.16.3". This answer is similar to the one from ShortRound1911.
server.js:
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const db = require('./src/config/db');
const routes = require('./src/routes');
const port = 3001;
const app = new express();
//...use body-parser
app.use(bodyParser.urlencoded({ extended: true }));
//...fire connection
mongoose.connect(db.url, (err, database) => {
if (err) return console.log(err);
//...fire the routes
app.use('/', routes);
app.listen(port, () => {
console.log('we are live on ' + port);
});
});
/src/routes/index.js:
const express = require('express');
const app = express();
const siswaRoute = require('./siswa_route');
app.get('/', (req, res) => {
res.json({item: 'Welcome ini separated page...'});
})
.use('/siswa', siswaRoute);
module.exports = app;
/src/routes/siswa_route.js:
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.json({item: 'Siswa page...'});
});
module.exports = app;
If you want a separate .js file to better organize your routes, just create a variable in the app.js file pointing to its location in the filesystem:
var wf = require(./routes/wf);
then,
app.get('/wf', wf.foo );
where .foo is some function declared in your wf.js file. e.g
// wf.js file
exports.foo = function(req,res){
console.log(` request object is ${req}, response object is ${res} `);
}
One tweak to all of these answers:
var routes = fs.readdirSync('routes')
.filter(function(v){
return (/.js$/).test(v);
});
Just use a regex to filter via testing each file in the array. It is not recursive, but it will filter out folders that don't end in .js
I know this is an old question, but I was trying to figure out something like for myself and this is the place I ended up on, so I wanted to put my solution to a similar problem in case someone else has the same issues I'm having. There's a nice node module out there called consign that does a lot of the file system stuff that is seen here for you (ie - no readdirSync stuff). For example:
I have a restful API application I'm trying to build and I want to put all of the requests that go to '/api/*' to be authenticated and I want to store all of my routes that go in api into their own directory (let's just call it 'api'). In the main part of the app:
app.use('/api', [authenticationMiddlewareFunction], require('./routes/api'));
Inside of the routes directory, I have a directory called "api" and a file called api.js. In api.js, I simply have:
var express = require('express');
var router = express.Router();
var consign = require('consign');
// get all routes inside the api directory and attach them to the api router
// all of these routes should be behind authorization
consign({cwd: 'routes'})
.include('api')
.into(router);
module.exports = router;
Everything worked as expected. Hope this helps someone.
index.js
const express = require("express");
const app = express();
const http = require('http');
const server = http.createServer(app).listen(3000);
const router = (global.router = (express.Router()));
app.use('/books', require('./routes/books'))
app.use('/users', require('./routes/users'))
app.use(router);
routes/users.js
const router = global.router
router.get('/', (req, res) => {
res.jsonp({name: 'John Smith'})
}
module.exports = router
routes/books.js
const router = global.router
router.get('/', (req, res) => {
res.jsonp({name: 'Dreams from My Father by Barack Obama'})
}
module.exports = router
if you have your server running local (http://localhost:3000) then
// Users
curl --request GET 'localhost:3000/users' => {name: 'John Smith'}
// Books
curl --request GET 'localhost:3000/books' => {name: 'Dreams from My Father by Barack Obama'}
I wrote a small plugin for doing this! got sick of writing the same code over and over.
https://www.npmjs.com/package/js-file-req
Hope it helps.
you can put all route functions in other files(modules) , and link it to the main server file.
in the main express file, add a function that will link the module to the server:
function link_routes(app, route_collection){
route_collection['get'].forEach(route => app.get(route.path, route.func));
route_collection['post'].forEach(route => app.post(route.path, route.func));
route_collection['delete'].forEach(route => app.delete(route.path, route.func));
route_collection['put'].forEach(route => app.put(route.path, route.func));
}
and call that function for each route model:
link_routes(app, require('./login.js'))
in the module files(for example - login.js file), define the functions as usual:
const login_screen = (req, res) => {
res.sendFile(`${__dirname}/pages/login.html`);
};
const forgot_password = (req, res) => {
console.log('we will reset the password here')
}
and export it with the request method as a key and the value is an array of objects, each with path and function keys.
module.exports = {
get: [{path:'/',func:login_screen}, {...} ],
post: [{path:'/login:forgotPassword', func:forgot_password}]
};

Resources