Nodejs pass variable from router file to another router file - node.js

i need to pass a variable called path from router file lessonsRouter.js to another router file lessons.js
My folder structure
routes
|
+--lessons.js
|
+--lessonsRouter.js
lessonsRouter.js
const router = require('express').Router();
const lessons = require('./lessons');
router.get('/*/([A-Z][a-z]+)/README.md', (req, res) => {
const path = req.url; // the variable that i need to pass to lessons.js router
// ignore next three lines
const pathSeparate = req.url.split('/');
const LessonTopic = pathSeparate[1].toLowerCase()
const LessonName = pathSeparate[2].match(/[A-Z][a-z]+/g).join('-').toLowerCase();
res.locals.path = path;
res.redirect(`/lessons/${LessonTopic}/${LessonName}`);
});
router.use('/:topic/:name', lessons);
module.exports = router;
lessons.js
const router = require('express').Router();
router.get('/', (req, res) => {
// i want to use variable path from lessonsRouter.js here
});
module.exports = router;

I would argue that you have your dependency/import direction wrong. lessons should import from lessonsRouter. But if you really want it this way, then you can export a function to which you can pass the path once you have it.
lessonsRouter.js
const lessons = require('./lessons');
router.get('/*/([A-Z][a-z]+)/README.md', (req, res) => {
const path = req.url; // the variable that i need to pass to lessons.js router
lessons(path);
...
});
lessons.js
module.exports = (path) => {
router.get('/', (req, res) => {
console.log(path);
});
};

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

What is the best or ideal approach to creating routes in node for production application?

Is it to create a single js file, then put every possible combination of routes for each verb like so:
myRoutes.js:
app.get('/', ...)
app.get('/students', ...)
app.get('/teachers', ...)
OR create something like this one below which is quite confusing for some beginner like me?
students.js:
const express = require('express');
const router = express.Router();
module.exports = () => {
router.get('/', (req, res) => {
return res.send('In students file');
});
}
teachers.js:
const express = require('express');
const router = express.Router();
module.exports = () => {
router.get('/', (req, res) => {
return res.send('In teachers file');
});
}
index.js:
const express = require('express');
const studentsRoute = require('./students');
const teachersRoute = require('./teachers');
const router = express.Router();
module.exports = () => {
router.get('/',(req,res) => {
res.send('In home directory');
});
}
router.use('/students', studentsRoute());
router.use('/teachers', teachersRoute());
What's a bit confusing is the router.get with '/' on both students and teachers file because '/' denotes HOME page, but in each of those files, there is this router.use('/students'...) and router.use('.teachers'...), so does the router.use supersede the router.get, and this is the ideal approach of structuring node folder and files in a real-world scenario?
Thanks
Split your routes by common path ex. /api/students
// app.js
const express = require('express')
const routes = require('./routes')
const app = express()
app.use('/api', routes)
// routes/index.js
const { Router } = require('express')
const studentsRouter = require('./students')
const router = Router()
router.use('/students', studentsRouter)
module.exports = router
// routes/students.js
const { Router } = require('express')
const api = Router()
api.get('/', (req, res) => {
return res.send('In students file')
})
module.exports = api
The beauty of this approach is that you can paste desired middleware for particular route /api/students/100 or route group /api/students or whole app /api
You can go even further and try to implement Controllers and Services pattern.
// routes/students.js
const { Router } = require('express')
const StudentsController = require('../controllers/students')
const api = Router()
api.get('/:id', StudentsController.getOne)
module.exports = api
// controllers/students.js
const studentsService = require('../services/students')
class StudentsController {
static getOne(req, res) {
const id = req.params.id
const student = studentsService.getOne(id)
res.send(student)
}
}
// services/students.js
class StudentsService {
constructor() {
// initialize with some parameters
}
getOne(id) {
// get from database a student with id
}
}
const studentsService = new StudentsService()
module.exports = studentsService

Importing and Exporting Module Variables - Node.js

I have a route of type post, that is receiving some info from the front-end, as shown below:
const router = require("express").Router();
const UsernameController = require("../controllers/username.controller");
router.post("/username", async (req, res) => {
try {
const cookie = req.cookies;
const {userName} = req.body;
let allGames = await new UsernameController().getGames(userName);
console.log(allGames[0].games)
return res.sendStatus(200)
} catch (err) {
console.log(err);
res.status(422).send(err);
};
});
module.exports = router;
I need to use the destructured {userName} = req.body in another file. So Iā€™m wondering how I can export the {userName} received from the front-end to the middleware.js file.
middleware.js:
const AuthController = require("../controllers/auth.controller");
const UsernameController = require("../controllers/username.controller");
const usernameRouter = require('../routes/username.route')
module.exports = async (req, res, next) => {
let userName = usernameRouter.userName
const userGamesArray = await new UsernameController().getGames(userName)
req.userGamesArray = userGamesArray;
next();
};
When I console.log the userName variable in the middleware file, it responds with undefined which means Iā€™m importing the variable wrongly from the route.js file.
Kindly assist me with importing the variable from the route.js file to the middleware.js file.
You just need to use a middleware in this /username route like this:
const middleware = require("./middleware"); // correct this path to a real one
router.post("/username", middleware, async (req, res) => {
...

How do we pass parameters to a mounted route in nodeJS?

I'm taking a course on NodeJS, there were a few assignments related to routing, everything works fine except this part which seems a little odd: For some reason, I cannot read the parameter ID being passed to the mounted router.
dish.js
const express = require('express');
const bodyParser = require('body-parser');
const dishRouter = express.Router();
dishRouter.use(bodyParser.json());
dishRouter.route('/')
.all((req,res,next) => {
res.statusCode = 200;
res.setHeader('Content-Type','text/plain');
next();
})
.get((req,res) => {
console.info('Info: ',req);
res.end(`Sending details of the dish back to you: ${req.params.dishId}`);
})
.post((req,res) => {
res.statusCode = 403;
res.end(`Operation not supported: ${req.params.dishId}`);
})
.put((req,res) => {
res.write(`Updating the dish...: ${req.params.dishId} \n` );
res.end(`Will update this dish: ${req.body.name} with details: ${req.body.description}`);
})
.delete((req,res) => {
res.end(`Deleting this dish: ${req.params.dishId}`);
});
exports.dish = dishRouter;
dishes.js
const express = require('express');
const bodyParser = require('body-parser');
const dishesRouter = express.Router();
dishesRouter.use(bodyParser.json());
dishesRouter.route('/')
.all((req,res,next) => {
res.statusCode = 200;
res.setHeader('Content-Type','text/plain');
next();
})
.get((req,res) => {
res.end('Sending all dishes back to you');
})
.post((req,res) => {
res.end(`Will add the dish: ${req.body.name} with details: ${req.body.description}`);
})
.put((req,res) => {
res.statusCode = 403;
res.end(`Operation not supported.`);
})
.delete((req,res) => {
res.end(`Deleting all dishes.....`);
});
exports.dishes = dishesRouter;
index.js
const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const http = require('http');
const dishRouter = require('./routes/dish');
const dishesRouter = require('./routes/dishes');
const hostname = 'localhost';
const port = 3000;
const app = express();
app.use(morgan('dev'));
app.use(bodyParser.json());
app.use('/dishes',dishesRouter.dishes);
app.use('/dishes/:dishId',dishRouter.dish);
app.use(express.static(__dirname+'/public'));
app.use((req,res,next) => {
res.statusCode = 200;
res.setHeader('Content-Type','text/html');
res.end('<html><body><h1>This is an Express Server</h1></body></html>');
});
const server = http.createServer(app);
server.listen(port,hostname,(req,res) => {
console.info(`Server running on port: ${port}, at: ${hostname}`);
})
This GET localhost:3000/dishes/123 is calling the right route, but the parameter dishId comes back as "undefined". Again, just learning nodeJS, seems like my receiver/mounted route should receive those parameters just fine, the body can be read properly, but not the params. ... thanks.
Yeah the params don't flow between routers. You're on a new router, hence new route params object.
You can check out the code for this:
https://github.com/expressjs/express/blob/master/lib/router/index.js#L43
Check out line 43 and line 53 where route.params is set to an empty object.
Some examples:
index.js
app.use('/dishes/:dishId',(req, res) => {
console.log('now I get my dishId', req.params.dishId)
});
dish.js (version 1)
dishRouter.route('/')
.get((req, res) => {
console.log('now i get nothing', req.params)
})
dish.js (version 2)
dishRouter.route('/:anotherId')
.get((req, res) => {
console.log('now we get another parameter', req.params.anotherId)
})
// the path would be /dish/123/456
I'm not sure if there is a offical-expressjs-way to pass the params object between routers.
One solution would be to create a custom handler
index.js
app.use('/dishes/:dishId', handler)
handler.js
function handler (req, res, next) {
if (req.method === 'GET') {
console.log('now we get it', req.params)
}
}
module.exports = handler
Anoter way would be to add the dishId to the request object before calling the router:
index.js
app.use('/dishes/:dishId', (req, res, next) => {
req.dishId = req.params.dishId
router(req, res, next)
})
dish.js
const express = require('express')
const router = express.Router()
router.route('/')
.get((req, res) => {
console.log('nothing here', req.params)
console.log('dishId', req.dishId)
})
module.exports = router
Third way would be to send the params as options to a router function
index.js
app.use('/dishes/:dishId', (req, res, next) => {
router(req.params)(req, res, next)
})
dish.js
function createRouter (options) {
const router = express.Router()
router.route('/')
.get((req, res) => {
console.log('nothing here', req.params)
console.log('but alot here', options)
})
return router
}
module.exports = createRouter
If you want you could also just put the :dishId on the router as an optional parameter
index.js
app.use('/dishes', dishesRouter)
dishes.js
const express = require('express')
const router = express.Router()
router.route('/:dishId?')
.get((req, res) => {
if (req.params.dishId) {
res.end(`Sending details of the dish back to you: ${req.params.dishId}`)
} else {
res.end('Sending all dishes back to you');
}
})
module.exports = router

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

Resources