Nodejs Use a middleware inside another - node.js

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
}

Related

Node js Error Handler Doesnt get exact error message from Controller Express/Mongoose

I a trying to implement a rest API for our project then I go for node js and express. I have built all the models and controllers. I faced an issue while trying to handle an error. Errorhandler function doesn't receive all the properties of error that caught in try/catch block. I can not read its name in a handler but I can use its name in the controller. Could you please help me?
const errorHandler = (err, req, res, next) => {
console.log(`Error in method:${req.method}: ${err.stack}`.bgRed);
let error = { ...err };
console.log(`Error handler: ${err.name}`);
res.status(error.statusCode || 500).json({
success: false,
data: error.message || 'Server Error',
});
};
module.exports = errorHandler;
controller
const mongoose = require('mongoose');
const Product = require('../models/Product');
const ErrorResponse = require('../utils/error');
const routeName = 'PRODUCT';
// #desc getting single product via id
// #route GET api/v1/products
// #acces public
exports.getProdcut = async (req, res, next) => {
try {
const product = await Product.findById(req.params.id);
if (!product) {
return next(
new ErrorResponse(`Product not found with id:${req.params.id}`, 404)
);
}
res.status(200).json({
success: true,
data: product,
});
} catch (err) {
console.log(err.name);
console.log('ERRO APPEND');
next(new ErrorResponse(`Product not found with id:${req.params.id}`, 404));
}
};
Assuming that errorHandler is part of your middleware that is somewhere after getProdcut, you can try just throwing the error and Express will automatically detect that for you, because error handling middleware such as yours accepts 4 parameters. So the following would work:
const getProdcut = async (req, res, next) => {
try {
// ...
} catch (err) {
throw err;
}
};
const errorHandler = (err, req, res, next) => {
if (err) {
console.log('hello from the error middleware');
console.log(err.name);
}
else {
// next() or some other logic here
}
}
app.use('/yourRoute', getProdcut, errorHandler);
And inside of your errorHandler you should have access to the error object.
Error-handling middleware always takes four arguments. You must provide four arguments to identify it as an error-handling middleware function. Even if you don’t need to use the next object, you must specify it to maintain the signature. Otherwise, the next object will be interpreted as regular middleware and will fail to handle errors.
https://expressjs.com/en/guide/using-middleware.html#middleware.error-handling

TypeError : next() is not a function

I am writing a middleware function that looks for validation errors and if the error is found gives out a certain output else continues the program flow. I have two functions with the exact code but they check for different schemas.
My first function runs without any exception. However, when I try to execute the second function I get an error in the console.
const validateCampground = (req, res, next) => {
const { error } = campgroundSchema.validate(req.body);
if (error) {
const msg = error.details.map((el) => el.message).join(",");
throw new ExpressError(msg, 400);
} else {
next();
}
};
const validateReview = (req, res, next) => {
const { error } = reviewSchema.validate(req.body);
if (error) {
const msg = error.details.map((el) => el.message).join(",");
throw new ExpressError(msg, 400);
} else {
next(); //this is the point where the exception occurs
}
};
It is only inside the validateReview function where next middleware function is not recognised as a valid function.
The problem was not with the next() middleware but instead it was with the route as I was wrapping the route with the validateReview function.
I was doing something like this :
app.post(
"/campgrounds/:id/reviews",
validateReview(
catchAsync(async (req, res) => {
//my Logic here
})
));
Whereas , I should have been doing something like this :
app.post(
"/campgrounds/:id/reviews",
validateReview,
catchAsync(async (req, res) => {
//my logic here
})
);
hi if you want to use a middileware
exports.middileware = (req,res,next)=>{
try{
//middileware logic
next();
}catch(err){
//print the error
})
}
}
and call the exported middileware file in requires file to check the middileware function
const { middileware } = require('path');
and use like this
router.get('/routename',middleware,nextfunction) //router you can choose as you like get,post,patch anything
try this out
I got this error when I omitted "req" and "res" in the function's parameters. When I added them, the error disappeared. Since I was using typescript, the first scenario looked like this:
function traceRoute(next){
console.log(routeTrace);
next();
}
Corrected to:
function traceRoute(req, res, next){
console.log(routeTrace);
next();
}

How to invoke multiple express middlewares manually

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

Calling an API endpoint from within another route in Node / Express

I have myRoute.js with a route (GET) defined and I want to call an api endpoint from another route (api.js), and I'm not sure what the right way to do this is. The api.js route is working properly (image and code below).
api.js
router.get('/getGroups/:uid', function(req, res, next) {
let uid = req.params.uid;
db.getAllGroups(uid).then((data) => {
let response =[];
for (i in data) {
response.push(data[i].groupname);
}
res.status(200).send(response);
})
.catch(function (err) {
return err;
});
});
works as expected:
myRoute.js
I would like when a user goes to localhost:3000/USER_ID that the route definition gets information from the api. Psuedo code below (someFunction).
router.get('/:uid', function(req, res, next) {
let uid = req.params.uid;
let fromApi = someFunction(`localhost:3000/getAllGroups/${uid}`); // <--!!!
console.log(fromApi) ; //expecting array
res.render('./personal/index.jade', {fromApi JSON stringified});
});
Not sure if i understand you correct but anyway i will try to help. So you have an api like
router.get('/getGroups/:uid', function(req, res, next) {
let uid = req.params.uid;
db.getAllGroups(uid).then((data) => {
let response =[];
for (i in data) {
response.push(data[i].groupname);
}
res.status(200).send(response);
})
.catch(function (err) {
return err;
});
});
If you would like to reuse it you can extract a function from the code above like so:
async function getAllGroupsByUserId(uid){
const result = [];
try{
const data = await db.getAllGroups(uid);
for (i in data) {
result.push(data[i].groupname);
};
return result;
}
catch(e) {
return e;
}
}
And then reuse it in your api & anywhere you want:
router.get('/getGroups/:uid', async function(req, res, next) {
const uid = req.params.uid;
const groups = await getAllGroupsByUserId(uid);
res.status(200).send(groups);
})
Same you can do in your another route:
router.get('/:uid', async function(req, res, next) {
const uid = req.params.uid;
const fromApi = await getAllGroupsByUserId(uid); // <--!!!
console.log(fromApi) ; //expecting array
res.render('./personal/index.jade', {fromApi JSON stringified});
});
Seems like pretty clear :)
I would use fetch for this. You can replace someFunction with fetch, and then put the res.render code in a .then(). So, you would get this:
const fetch = require("node-fetch");
router.get('/:uid', function(req, res, next) {
let uid = req.params.uid;
fetch('localhost:3000/getAllGroups/${uid}').then(res => res.json()).then(function(data) {
returned = data.json();
console.log(returned); //expecting array
res.render('./personal/index.jade', {JSON.stringify(returned)});
});
});
A more robust way with error handling would be to write something like this:
const fetch = require("node-fetch");
function handleErrors(response) {
if(!response.ok) {
throw new Error("Request failed " + response.statusText);
}
return response;
}
router.get('/:uid', function(req, res, next) {
let uid = req.params.uid;
fetch('localhost:3000/getAllGroups/${uid}')
.then(handleErrors)
.then(res => res.json())
.then(function(data) {
console.log(data) ; //expecting array
res.render('./personal/index.jade', {JSON.stringify(data)});
})
.catch(function(err) {
// handle the error here
})
});
The ideal way would be to abstract your code into a method so you aren't calling yourself, as The Reason said. However, if you really want to call yourself, this will work.

Express: Show value from external request as response

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

Resources