Handling Express Validator in a middleware - javascript

The classic way shown in tutorials would be:
Router.post('/add-post', addPostValidation(), addPost)
But what if I want to do the validation in a middleware like this:
The router:
Router.post('/add-post', addPost)
The middleware:
module.exports = (req, res, next) => {
if(req.method == 'POST') {
console.log('hello')
body('name').notEmpty()
let result = validationResult(req)
console.log(result)
}
next()
}
The "hello" shows up but the result won't show me any error if I do this way

You can access a validation chain's run function to do what you want to do.
const addPost = (req, res, next) => {
const validations = [
body('name').notEmpty(),
body('content').notEmpty(),
];
await Promise.all(validations.map(chain => chain.run(req)));
const result = validationResult(req);
// ...
};
Docs

Related

req.body undefined in custom middleware express

So I'm doing a simple NodeJS app with MongoDB/Express/Mongoose. In my mongoose schema I have a field (pictureURL) with a default value, the problem is that if pictureURL is an empty string the default value does not get applied. To solve this I though about using a custom middleware when doing the POST request either when creating or updating the model.
But the issue I'm having is that from within the middleware req.body is undefined. It is fine when in the router.post method but not in the middleware. Here is the code I have.
middleware (pictureU.js)
const app = require('../app');
const bookPictureUrl = (res, req, next) => {
console.log({ body: req.body });
if (!req.body.pictureUrl)
req.body.pictureUrl = 'images/default';
next();
};
module.exports = { bookPictureUrl };
book.routes.js
const app = require('../app');
const router = require('express').Router();
const Book = require('../models/Book.model');
const { bookPictureUrl } = require('../middleware/pictureUrl');
router.post('/update/:id', bookPictureUrl, async (req, res, next) => {
try {
req.body.authors = req.body.authors.split(',');
const data = await Book.findByIdAndUpdate(req.params.id, req.body);
res.redirect('/books');
} catch (err) {
next(err);
}
});
Any help trying to fix this so that I can use req.body within the middleware would be greatly appreciate.
Thanks
You mixed up your argument order. req should come before res.
const bookPictureUrl = (req, res, next) => {
console.log({ body: req.body });
if (!req.body.pictureUrl)
req.body.pictureUrl = 'images/default';
next();
};

How to get session variable in app.post request?

I would like to get the data from session variable (req.user.username) then use it for posting. I'm using passportjs as authentication. I'm using router. Here is my code:
router.use('/login', passport.authenticate("local-register", async (err, user, info) => {
if (err) {
return next('Error');
}
if (!user) {
return next('Error');
}
req.user = user;
return req.login(user, (error: Error) => {
if (error) {
return next('Error');
}
return req.session.save((erro: Error) => {
if (erro) {
return next('Error');
}
return next();
});
});
})(req, res, next);)
router.get('/', async (req, res) => {
console.log(req.user.username) // working just fine
});
router.post('/upload', async (req, res) => {
const uploaderName = req.user.username // I'm getting undefined
const upload = await database.query('INSERT INTO user WHERE username=$1', [uploaderName])
console.log(uploaderName);
})
So I finally found the answer to the question. For those who will encounter the problem in the future. You just add the session middleware AGAIN on the top of the routes. If your routes are separated to the main server file.
/src/routes/routes.ts -> add again the middleware on top.
const app = router();
app.use(sessions) // -> right here you need to add the middleware again to //access the req.user session variable
app.get('/', async (req, res) => {
console.log(req.user.username) // working just fine
});
app.post('/upload', async (req, res) => {
const uploaderName = req.user.username // I'm getting undefined
const upload = await database.query('INSERT INTO user WHERE username=$1', [uploaderName])
console.log(uploaderName);
})

express.js async router and error handling

I have an async function as a route handler, and i'd like to have errors handled as some kind of middleware. Here is my working attempt:
router.get(
"/",
asyncMiddleware(
routeProviderMiddleware(
async ({ y }) => ({
body: await db.query({x: y})
})
)
)
)
// This is the middleware that catches any errors from the business logic and calls next to render the error page
const asyncMiddleware = fn =>
(req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch(next)
}
// This is a middleware that provides the route handler with the query and maybe some other services that I don't want the route handler to explicitly access to
const routeProviderMiddleware = routeHandlerFn => async (req, res) => {
const {status = 200, body = {}} = await routeHandlerFn(req.query)
res.status(status).json(body)
}
What I strive to is a way to make the route declaration cleaner - I don't want the 2 middleware wrappers there, ideally i'd like for the business logic function there only, and somehow declare that every route is wrapped in these.
Even combining the two middlewares together would be nice, but I didn't manage.
I use following approach:
Create asyncWrap as helper middleware:
const asyncWrap = fn =>
function asyncUtilWrap (req, res, next, ...args) {
const fnReturn = fn(req, res, next, ...args)
return Promise.resolve(fnReturn).catch(next)
}
module.exports = asyncWrap
All your routes/middlewares/controllers should use this asyncWrap to handle errors:
router.get('/', asyncWrap(async (req, res, next) => {
let result = await db.query({x: y})
res.send(result)
}));
At app.js, the last middleware will receive the errors of all asyncWrap:
// 500 Internal Errors
app.use((err, req, res, next) => {
res.status(err.status || 500)
res.send({
message: err.message,
errors: err.errors,
})
})
Express 5 automatically handles async errors correctly
https://expressjs.com/en/guide/error-handling.html currently says it clearly:
Starting with Express 5, route handlers and middleware that return a Promise will call next(value) automatically when they reject or throw an error. For example:
app.get('/user/:id', async function (req, res, next) {
var user = await getUserById(req.params.id)
res.send(user)
})
If getUserById throws an error or rejects, next will be called with either the thrown error or the rejected value. If no rejected value is provided, next will be called with a default Error object provided by the Express router.
I have shown that in an experiment at: Passing in Async functions to Node.js Express.js router
This means that you will be able to just make the callback async and use await from it directly without any extra wrappers:
router.get("/", async (req, res) =>
const obj = await db.query({x: req.params.id})
// Use obj normally.
)
and errors will be correctly handled automatically.
Express permits a list of middlewares for a route and this approach sometimes works for me better than higher-order functions (they sometimes look like an overengineering).
Example:
app.get('/',
validate,
process,
serveJson)
function validate(req, res, next) {
const query = req.query;
if (isEmpty(query)) {
return res.status(400).end();
}
res.locals.y = query;
next();
}
function process(req, res, next) {
Promise.resolve()
.then(async () => {
res.locals.data = await db.query({x: res.locals.y});
next();
})
.catch((err) =>
res.status(503).end()
);
}
function serveJson(req, res, next) {
res.status(200).json(res.locals.data);
}
What you can do is add an error handlers after your routes. https://expressjs.com/en/guide/error-handling.html
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
What I ended up doing is unifying the wrappers like this:
const routeProvider = routeHandlerFn => async (req, res, next) => {
try {
const {status = 200, body = {}} = await routeHandlerFn(req.query)
res.status(status).json(body)
} catch(error) {
next(error)
}
}
This wrapper is all any route would need. It catches unexpected errors and provides the route handler with the needed params.

JS: How can i save a value from a return nested function

I use NextJS and React. My server.js file, looks like this:
const express = require('express')
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare()
.then(() => {
const server = express()
var countrycode = ''
server.post('/', (req, res) => {
countrycode = req.body.code
})
server.get('/', (req, res) => {
console.log(res)
if (countrycode == 'DE') {
return app.render(req, res, '/de', req.query)
} else {
return app.render(req, res, '/', req.query)
}
})
})
})
i try to save the req.body.code value inside the outside variable var countrycode but it doesn't work. I need to take this step so I can check this value in the server.get function. If the client comes from Germany, the German side should be returned, otherwise the English.
Where is my mistake? What exactly do I need to change?
Thanks for your answer
If you are trying to serve a specific version of your application based on the user's language preference, you can use the Accept-Language header which is sent by the browser. This header contains the preferred languages of the user as configured in the browser. For convenience it I would recommend using some kind of Express middleware like express-request-language.
In your case this could would look something like this:
const express = require('express')
const next = require('next')
const requestLanguage = require('express-request-language')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
app.prepare()
.then(() => {
const server = express()
server.use(requestLanguage({
languages: ['en-US', 'de-DE']
}))
server.get('/', (req, res) => {
switch (req.language) {
case 'de-DE':
return app.render(req, res, '/de', req.query)
default:
return app.render(req, res, '/', req.query)
}
})
})
It's difficult to be sure without seeing the portion of code where you attempt to use the value stored in countrycode.
My guess is that you're trying to read the value before it is set by the function. For example:
var countrycode;
server.post('/', (req, res) => {
countrycode = req.body.code
})
console.log(countrycode);
Here, the console output will be undefined, because countrycode will not be set until the POST completes and the function sets its value. To fix this, you should look into using a promise which will only resolve once the value of countrycode has been set.
server.post() is an asynchronous function, which returns immediately and cannot assign values to variables in an outer synchronous code block. Restructure your code to deal with asynchronous behavior:
app.prepare().then(() => {
const server = express()
return new Promise((resolve, reject) => {
server.post('/', (req, res) => {
resolve(req.body.code);
})
});
}).then(countrycode => {
console.log('server responded with countrycode:', countrycode);
})
Alternatively:
app.prepare()
.then(() => {
const server = express()
return new Promise(resolve => {
server.post('/', (req, res) => {
resolve(req.body.code);
})
})
.then(countrycode => {
return new Promise(resolve => {
server.get('/', (req, res) => {
console.log(res)
if (countrycode == 'DE') {
resolve(app.render(req, res, '/de', req.query))
} else {
resolve(app.render(req, res, '/', req.query))
}
})
})
})
})

Pass values from referenced function in promise-chain?

I'm writing some rendering-code for an Express app, I wish to catch errors and then output them in the function render, but I'm not sure how I'm going to move them from one method to the other.
app.get('/user/makeRider', auth,
(req, res, next) => {
req.user.user.makeRider(req.query)
.catch(error)
.then(render(req, res));
}
);
var render = (req, res) => {
var response = {
params: req.query,
user: req.user.fulluser
};
res.json(response);
},
error = (reason) => {
reason.errors.forEach((error) =>{
console.log(error);
});
return;
};
You can use error function as your last midleware in the chain and simply pass the request to the next chain:
var render = (req, res) => {
var response = {
params: req.query,
user: req.user.fulluser
};
res.json(response);
}
app.get('/user/makeRider', auth,
(req, res, next) => {
req.user.user.makeRider(req.query)
.catch(next)
.then(render(req, res));
}
);
app.use((reason, req, res, next) => {
res.send(reason.errors);
// or you can send them as json: res.status(404).json({errors: reason.errors})
});
Beware of hoisting issue in your code, the variable declarations are hoisted to the top, but not their assignments, error and render function may appear as undefined when accessed from your route.
A quick, but maybe not the most elegant solution would be to add errors as a parameter to your render function, then you could do something like this:
app.get('/user/makeRider', auth,
(req, res, next) => {
req.user.user.makeRider(req.query)
.catch((reason)=>{
render(req, res, reason.errors)
})
.then(render(req, res));
}
);
var render = (req, res, errs) => {
var response = {
params: req.query,
user: req.user.fulluser
};
res.json(response);
};

Categories