I have a situation where I want to invoke multiple express middlewares depends on the request payload.
These middlewares are generated from the express validator checkSchema
method.
So I have written a middleware which gets access to the request object and I can read a property from the request payload then take a decision on which schema has to be run.
An implementation would like this.
let app = express();
let schema1 = checkSchema({
field1: {
in: ["body"],
exists: {
errorMessage: "field1 is missing",
}
}
});
let schema2 = checkSchema({
field2: {
in: ["body"],
exists: {
errorMessage: "field 2 is missing",
}
}
});
app.post("/resource", (req, res, next) => {
if(req.body.type === "TYPE1") {
// Invoke schema1 middleware
}
if(req.body.type === "TYPE2") {
// Invoke schema2 middleware
}
});
Here schema1 and schema 2 are not single middleware. It is a
middleware array.
If it was middleware, I could call schema1(req, res, next);
If anybody has gone through this, Please suggest me what is the approach to run a middleware array manually.
I have released express-validator v6.0.0, which should help addressing this kind of thing.
Now there is a .run(req) method, which should let you do things with express-validator in an imperative way.
For your use case, you could do the following:
app.post("/resource", async (req, res, next) => {
if(req.body.type === "TYPE1") {
await Promise.all(schema1.map(chain => chain.run(req)));
}
if(req.body.type === "TYPE2") {
await Promise.all(schema2.map(chain => chain.run(req)));
}
});
Since checkSchema returns an array of validation chains, the code is mapping each of them to their respective execution promise.
When all of the promises are finished, your code can continue executing and do whatever you want. Maybe check if there are errors with validationResult, render a different page accordingly, etc -- up to you!
According to this question Use an array of middlewares at express.js there is one repo: https://github.com/blakeembrey/compose-middleware:
According to the readme:
app.use(compose([
function (req, res, next) {},
function (err, req, res, next) {},
function (req, res, next) {}
]))
So, what you can do is:
app.post("/resource", (req, res, next) => {
if(req.body.type === "TYPE1") {
compose(schema1)(req,res,next);
}
if(req.body.type === "TYPE2") {
compose(schema2)(req,res,next);
}
});
Related
I'm having some problems using 2 middlewares inside the same function, already tried to search for all internet and didn't find a useful solution.
validator file
module.exports = {
create: async (req, res, next) => {
await celebrate(options.create)(req, res, next);
return res.status(500).json({ message: 'middleware 2'});
},
}
routes file
routes.post('/user', UserValidator.Create ,UserController.create);
The celebrate lib filters some basic validations like string lenght, null values, etc. And the celebrate() function returns another function with the (req, res, next) params.
When the celebrate returns the validation error, it stills continues to execute the code, so it tries to execute the next return and I get an error because the return has already been sent.
When using separate middlewares in the routes, it works normally:
routes.post('/user', celebrate(...), middleware2 ,UserController.create);
I also tried this way but the same thing happens, but now without an error, just returning the middleware2 result.
module.exports = {
create: async (req, res, next) => {
await celebrate(options.create)(req, res, () => {
return res.status(500).json({ message: 'middleware 2'});
});
},
Is there a way to fix this?
u should try this structure
// API
app.post('/something', Middleware.validate, Controller.create)
//Middleware
const validate = (req, res, done) => {
const errorArray = []
const body = req.body
// identifier is required, Validating as String, and length range.
if (!_.isString(body.identifier) || body.identifier.length < 2 || body.identifier.length > 10) {
errorArray.push({
field: 'identifier',
error: 70000,
message: 'Please provide only valid \'identifier\' as string, length must be between 2 and 10.'
})
}
if (!_.isEmpty(errorArray)) {
return errorArray
}
done()
}
module.exports = {
validate
}
// Controller
const create = function (req, res) {
return // your functionality
}
module.exports = {
create
}
I'm writing middleware that I'm applying at the route level, like so:
router.get('/foo', myMiddleware, (req, res) => { ... });
so I can do some stuff with the request. But I also need to catch errors to do some special handling. I know I can add a handler afterwards like this:
... (req, res) => { ... }, myErrorHandler);
and it'll get called just fine.
But my question is, is there any way to have a single piece of middleware that can do all of this so I don't need two points of integration? I tried calling req.on('error', (err) => { ... }) within my middleware but it never seems to be called.
Express comes with a built-in error handler that takes care of any errors that might be encountered in the app. This default error-handling middleware function is added at the end of the middleware function stack.
// Router
router.get('/foo', myMiddleware, (req, res) => { ... });
// Router Error Handler
router.use(function (err, req, res, next) {
});
I ended up solving this by writing a helper function that wraps the actual handler. It looks like this:
function checkPage(handler: express.RequestHandler) {
return async (req: express.Request, res: express.Response, next: express.NextFunction) => {
let _write = res.write;
res.write = chunk => {
if (req.query.verbose) {
return _write.call(res, `<p>${chunk}</p>`);
} else {
return true;
}
}
try {
await handler(req, res, next);
res.write('<hr/><p style="color:green;"><b>happy</b></p>');
} catch (err) {
res.write(`<p style="color:red;">${err}</p>`);
res.write('<hr/><p style="color:red;"><b>SAD!</b></p>')
}
res.end();
}
}
Then in my route handler, I just use it like so:
router.get('/foo', checkPage(async (req, res, next) => {
...
res.write('stuff');
...
}));
I'm actually using loopback, and here is my needs:
I have two middlewares triggered by a POST request on /api/Orders/, and I need middleware 1 to pass data to middleware 2.
For example:
middleware1.js
module.exports = function() {
return function firstMiddleware(req, res, next) {
var toPass= "string to pass to second middleware";
next();
};
}
middleware2.js
module.exports = function() {
return function secondMiddleware(req, res, next) {
//Do whatever to get passed value from middleware1
};
}
I did not found anything useful in the official documentation talking about this, but I may have missed it.
Thanks.
In middleware1 you can update req object and access the variable in the middleware2.
middleware1.js
module.exports = function() {
return function firstMiddleware(req, res, next) {
var toPass= "string to pass to second middleware";
req.locals = { toPass };
next();
};
}
middleware2.js
module.exports = function() {
return function secondMiddleware(req, res, next) {
console.log(req.locals.toPass);
next();
};
}
I have the following function where I am using the cryptocompare npm package:
getPrice: function(coin){
cc.price(coin, 'USD')
.then(prices => {
console.log(prices);
return prices;
}).catch(console.error)
}
// https://github.com/markusdanek/crypto-api/blob/master/server/helper/cryptocompare.js
Now I want to set up an Express server to open http://localhost:9000/current and to display the current "price".
So I have my controller which looks like this:
module.exports = {
getCurrentPrice: function(req, res, next) {
getPrice('ETH', function(price);
}
};
// https://github.com/markusdanek/crypto-api/blob/master/server/controllers/CryptoController.jshttps://github.com/markusdanek/crypto-api/blob/master/server/controllers/CryptoController.js
My route:
var controllers = require('../controllers'),
app = require('express').Router();
module.exports = function(app) {
app.get('/current', controllers.crypto.getCurrentPrice);
};
When I open now http://localhost:9000/current I only get the current price in my console, but not in my browser.
How can I also set the response to the value?
I tried this but failed:
module.exports = {
getCurrentPrice: function(req, res, next) {
getPrice('ETH', function(price){
res.status(200).json(price);
});
}
};
I guess thats the wrong way to call a callback.. do I have to modify my helper function or anything else?
My project is also on Github for further references: https://github.com/markusdanek/crypto-api
below may help you
module.exports = {
getCurrentPrice: function(req, res, next) {
cc.price('ETH', 'USD')
.then(prices => {
console.log(prices);
res.json(prices)
})
.catch(err=>{
console.error(err)
return next(err);
})
}
};
I'm using express with node and want to use a co/yield patter to wrangle my async callbacks.
The current code looks like this:
web.post('/request/path', function(req, res, next) {
co(function *() {
let body = req.body
let account = yield db.get('account', {key: body.account})
if (!account) {
throw new Error('Cannot find account')
}
let host = yield db.get('host', {key: body.hostname})
....
}).catch(err => {log.info(err) ; res.send({error: err})})
This is working really well, but I'd like to be able to simplify the first 2 lines:
web.post('/request/path', function(req, res, next) {
co(function *() {
Is it possible to somehow integrate the co(function *() into the first line? Does express provide support for co() and yielding functions?
You can use co-express along with promises.
Example,
router.get('/', wrap(function* (req, res, next) {
var val;
try {
val = yield aPromise();
} catch (e) {
return next(e);
}
res.send(val);
}));
You can simplify the syntax with an arrow function:
web.post('/request/path', (req, res, next) => co(function *() {
//...
}).catch(err => {log.info(err) ; res.send({error: err})})
I don't see any extra benefit to using another package. When async/await hits the shelves, we may see express get an update.
On a side note, making your own co-express is rather simple:
Consider 'co-express/index.js'
module.exports = generator => (req, res, next) => require('co').wrap(generator)(req, res, next).catch(err => res.status(500).send(err));
And now:
var coe = require('./co-express');
web.post('/request/path', coe(function *(req, res, next) {
//...
})
This way you've got the latest co package.