Hi I am trying to follow ES6 syntax to create a middleware of my node.js application.
index.js
export default class Middleware {
constructor() {
//do nothing
}
fun1 = (req, res, next) => {
console.log("------------------------------------");
console.log("AAa");
console.log("------------------------------------");
next();
};
fun2 = (req, res, next) => {
console.log("------------------------------------");
console.log("AAa");
console.log("------------------------------------");
next();
};
}
app.js
import Middleware from ".index";
app.use(Middleware);
I am getting an error Cannot call a class as a function. Does anyone know what is wrong?
Express app#use expects a function with the following signature:
function(req, res, next) {
To make it work, you need to do:
Create an instance of Middleware class.
Register middleware for each function in the class.
Example:
let middleware = new Middleware();
app.use(middleware.func1);
app.use(middleware.func2);
Related
In my Express application I implement routes in routes.ts:
var Router = express.Router();
Router.route('/models/:modelId')
.get(function (req, res) {
service.setParameter(req)
service.get(req,res)
});
Router.route('/models/:modelId')
.post(function (req, res) {
service.setParameter(req)
service.post(req,res)
});
And express.ts:
export const App = express()
App.use(express.json())
App.use(express.urlencoded({ extended: true }))
App.use(helmet())
App.use('/', Router)
At each router call I'd like to execute a piece of code service.setParameter(req) that gets particular parameter from 'params', but I don't want to add to each router method explicitly.
I tried adding it at as middleware before and after Router
App.use('/', Router)
App.use(function(req, res, next){
service.setParameter(req)
next()
})
But if I define it before Router then route hasn't been set yet, and I don't get the parameter I want, and if I define it after, then middleware is not executed.
How can execute service.setParameter(req) in a generic way so that it applies to all the routes?
In express.ts file, you can add a middleware that would do it before mounding the Router, and then just procced forward with next(). You can do it like this:
App.use('/*', (req, res, next) => {
service.setParameter(req);
next();
});
App.use('/', Router)
You need to place your custom middleware between the context path and your router inside app.use(..):
const router = express.Router();
router.post('/', (req, res) => {
service.post(req,res);
});
router.get('/', (req, res) => {
service.get(req,res)
});
app.use('/models', (req, res, next) => {
service.setParameter(req);
next();
}, router);
With above code the middleware will be excecuted for all requests to '/models'.
You can use app.use(async (req,res,next) => {...}) in order to declare a middleware that executes in all the requests, if you want this middleware to be called first, it must be declare before than your routes, the middleware have to call next() in order to continue with the execution flow, if you want to be called at the end of you request, you have to put at the end of your declarations but before of the error middleware, in that approach each route have to call next() at the end of your function.
First approach
const express = require('express');
const app = express();
const router = express.Router();
router.post('/', async (req, res) => {
await service.post(req,res);
});
router.get('/', async (req, res) => {
await service.get(req,res)
});
app.use((req,res,next) => {
console.log("always called");
next();
});
app.use('/',router);
Second approach
const express = require('express');
const app = express();
const router = express.Router();
router.post('/', async (req, res, next) => {
await service.post(req,res);
next();
});
router.get('/', async (req, res, next) => {
await service.get(req,res);
next();
});
app.use('/',router);
app.use((req,res) => {
console.log("always called");
});
Thanks for all the answers, they helped me better understand how routing works on Express.
I found another solution, which I think works best in my case - using Router.all() method:
const setRequest = function(req, res, next){
logger.setRequest(request)
next()
}
Router.route('/models/:model_id')
.all(setRequest)
.get(function (req, res) {service.execute()})
.put(function (req, res) {service.execute()})
app.js
// Calling Routes
require("./routes")(app);
router folder
index.js
module.exports = function (app) {
app.use("/", require("./all_routes"));
}
all_routes.js
var express = require("express");
var router = express.Router();
router.get("/", function (req, res, next) {
res.render("home/index.html");
});
//About Page
router.get("/about", function (req, res, next) {
res.render("about/index.html");
});
//Contact
router.get("/contact", function (req, res, next) {
res.render("contact/index.html");
});
//product
router.get("/product", function (req, res, next) {
res.render("product/index.html");
});
//product list
router.get("/product/demo-product", function (req, res, next) {
res.render("demo-product/index.html");
});
router.get("/product/request-product", function (req, res, next) {
res.render("request-product/index.html");
});
//service
router.get("/service", function (req, res, next) {
res.render("product/index.html");
});
//service list
router.get("/service/what-we-do", function (req, res, next) {
res.render("what-we-do/index.html");
});
router.get("/service/how-we-do", function (req, res, next) {
res.render("how-we-do/index.html");
});
I am trying to reduce the code in all_routes.js file has same code is repeating again and again
I searched online and trying to create it dynamically but getting no success is there any way I can reduce the line of code as I have given the follow of my code above
If you'd like to cut down on boilerplate of all your get routes, one option is to create an object to map your routes to the files they're loading. Then you can iterate over that object and add the routes to your router.
const routes = {
"/": "home/index.html",
"/about": "about/index.html",
"/contact": "contact/index.html"
// Add others here
}
for (let key in routes) {
router.get(key, function (req, res, next) {
res.render(routes[key]);
});
}
Edit: If your routes are consistent in that the index.html file will always be in the directory named after the part after the last / in your route, you can potentially use an array and some fancy logic. Just don't break the rule!
const routes = [
"/contact",
"/product",
"/product/demo-product",
"/product/request-product"
]
routes.forEach(route => {
const path = /[^/]*$/.exec(route)[0];
router.get(route, function (req, res, next) {
res.render(`${path}/index.html`);
});
})
I wanted to know if it is possible to pass middleware in the controller function
routes.js
const router = require('express').Router();
router.get('/home', home.get);
home.js
module.exports.get = (req, res) => {
res.send("Welcome to HOME");
};
I want to pass middleware in the get function of home.js, one method I know is to call the middleware function and then pass the controller function as next callback
module.exports.get = (req, res) => {
authCheckMiddleware(req, res, () => {
res.send("Welcome to HOME");
})
};
If there any approach? to pass the middleware in the function defination or something for home.js
You can do it by adding your middleware function as an argument preceding the actual controller:
router.get('/home', authCheckMiddleware, home.get);
I notice a recurring pattern in my express app which I think could be optimized. Basically I have a route calling a method with some asynchronous functions.
index.js
const controller = require('./controller.js');
const router = new Router();
router.post('/user', controller.createUser);
module.exports = router;
controller.js
exports.createUser = async (req, res, next) => {
try {
// asynchronous calls, etc.
} catch (e) {
// pass error to middleware
next(e);
}
}
The try/catch blocks are recurring in each of my controller methods. I'd want errors caught to be passed to my error-handling middleware. Therefore it seems impractical and repetitive to pass errors in each of my controller functions. Could I refactor this?
What if I wrap the controller method in a function as such:
index.js
const controller = require('./controller.js');
const router = new Router();
const handleErrors = (func) => async (req, res, next) => {
try { await func(req, res, next) }
catch (e) { return next(e) }
};
router.post('/user', handleErrors(controller.createUser));
module.exports = router;
controller.js
exports.createUser = async (req, res, next) => {
// asynchronous calls, etc.
if (a !== b) {
// errors can be passed to middleware as such
throw new CustomError(400, 'a is not equal to b');
}
}
Would this be an appropriate solution? Does Express have any built-in ways of accomplishing the same thing? Should I be cautious about refactoring my entire application in this way?
Would this be an appropriate solution?
Yes, looks nice.
Does Express have any built-in ways of accomplishing the same thing?
No, Express was written before async / await was introduced.
Should I be cautious about refactoring my entire application in this way?
I don't think so. How i would write that:
const handleErrors = (func) => (req, res, next) => func(req, res).then(() => next(), next);
I recommend you this article: https://medium.com/#Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016
As in the article, this should be the middleware:
const asyncMiddleware = fn =>
(req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch(next);
};
This is how a controller should look like:
router.get('/users/:id', asyncMiddleware(async (req, res, next) => {
/*
if there is an error thrown in getUserFromDb, asyncMiddleware
will pass it to next() and express will handle the error;
*/
const user = await getUserFromDb({ id: req.params.id })
res.json(user);
}));
router.post('/users', asyncMiddleware(async (req, res, next) => {
const user = await makeNewUser(req.body);
res.json(user)
}))
I'm trying to add an argument to a function passed along as an argument in an express js route.
This is an expressjs route:
app.get('path', someFunc,
function(req, res, next) {
res.render('layout');
});
The function someFunc right there takes the arguments req, res, next.
I want to add an additional argument to it. If I use apply or call it seems to replace all the existing arguments.
I want to be able to do:
someFunction (req, res, next, custom) {}
How can I do this? Thanks.
I am not sure that this is the best way but you could do something like this :
var someFunc = function (req, res, next, custom) { console.log(custom); next(); }
app.get('path',
function (req, res, next) { someFunc(req, res, next, 'custom') },
function(req, res, next) {
res.render('layout');
});
I would create a route like this:
// Inside your routes.js:
module.exports.someRoute = function(myArgument) {
return function(req, res, next) {
// Do whatever you want with myArgument.
};
};
// Inside your app.js:
app.get('path', routes.someRoute({ foo: 1 }));
This way your route setup is clear of any logic.