How to pass data from express-validation middleware to next function? - node.js

I have an express app that looks like this.
const app = require('express')();
// Task model
const Task = require('./models/Task');
const { param, validationResult } = require('express-validator');
const getTaskValidations = [
param('id')
.custom(async (id, { req }) => {
try {
const task = await Task.findOne({ _id: id, user: req.user.id });
if (!task) Promise.reject('Task not found');
} catch (err) {
// Handle error
}
})
]
const validate = (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(401).json({ message: errors.array()[0].msg });
}
next();
}
// Get Task by id
app.get('/tasks/:id', getTaskValidations, validate, async (req, res) => {
try {
const task = await Task.findById(req.params.id);
res.json(task)
} catch (err) {
// Handle err
}
})
I want to get the task by id. In GET tasks/:id the req.params.id will contain the id of the task.
The Task Model looks like this
{
id:
task:
user:
}
By looking at the endpoint it is clear that I'm passing two middlewares.
The first middleware getTaskValidations will check if the task will given id and req.user exists.
The second middleware validate will check for errors.
And then again will query database for task and send data to the client causing 2 database queries.
How can I reuse the same task obtained in the getTaskValidations middleware.

you can add result of query add to the req.body.task like this
const getTaskValidations = [
param('id')
.custom(async (id, { req }) => {
try {
const task = await Task.findOne({ _id: id, user: req.user.id });
req.body.task = task
if (!task) Promise.reject('Task not found');
} catch (err) {
// Handle error
}
})
]
in controller
app.get('/tasks/:id', getTaskValidations, validate, async (req, res) => {
try {
let {task} = req.body
res.json(task)
} catch (err) {
// Handle err
}
})

You can store the response you get from the query in the request(req).
const task = await Task.findOne({ _id: id, user: req.user.id });
if (task)
req.task = task;
else
Promise.reject('Task not found');
Later on, in the API endpoint, you can simply use
app.get('/tasks/:id', getTaskValidations, validate, async (req, res) => {
try {
const task = req.task;
res.json(task)
} catch (err) {
// Handle err
}
});
to get the task obtained in the getTaskValidations middleware.

Related

How to populate an id in an array of objects firebase

committeeHead is a reference in collection users. I want to populate this id to get the specific data.
tried using Promise.all but I don't completely understand it and it isn't working for me.
const getAllCommittees = async (req, res, next) => {
try {
const committees = await db.collection("committees").get();
const committeesArray = [];
committees.forEach((doc) => {
committeesArray.push({ id: doc.id, ...doc.data() });
});
const committeesWithUsers = await Promise.all(
committeesArray.map((committee) => {
const user = db.collection("users").doc(committee.committeeHead).get();
return {
committee,
user,
};
})
);
res.json(committeesWithUsers);
} catch (err) {
console.log(err);
next(err);
}
};

Filter method in MongoDB with optional fields

I want to write a method in node js with mongodb that filters users by
both batch and course
2)only batch if course not enterted
3)only course if batch not enterted
//filter students by either course or batch
export const getUsersByCourseOrBatch = async (req, res) => {
try {
const { course, batch } = req.body
if(course, batch)
{
const users = await UserModel.find({ course: course, batch: batch })
}
else if(course)
{
const users = await UserModel.find({ course: course })
}
else
{
const users = await UserModel.find({ batch: batch })
}
res.status(200).json({
message: `Users from course ${course} and batch ${batch}`,
users,
})
} catch (error) {
res.status(500).json({ message: error.message })
}
}
You can build a custom filter object:
export const getUsersByCourseOrBatch = async (req, res) => {
try {
const { course, batch } = req.body;
const filter = {};
if (course) filter.course = course;
if (batch) filter.batch = batch;
const users = await UserModel.find(filter);
res.status(200).json({
message: `Users from course ${course} and batch ${batch}`,
users,
});
} catch (error) {
res.status(500).json({ message: error.message });
}
};

Why does my register user post request fail with a 500 error?

I'm building an app with React and Node/Express, and I'm having trouble with my register user function. The data I am passing in is correct, and other endpoints work fine. The register one keeps returning a 500 error and I can't figure out why.
This is my request:
console.log(values)
axios
.post(
'https://foodtrackr-backend.herokuapp.com/api/register',
values
)
.then(res => {
console.log('res.data', res.data);
})
.catch(error => {
console.log('nope');
console.error(error);
});
};
and this is my endpoint:
router.post('/register', async (req, res) => {
let user = req.body;
const newUser = await Users.add(user);
try {
if (newUser) {
res.status(201).json(user);
} else res.status(404);
} catch (error) {
res.status(500).json('noooooo');
}
});
and this is my model:
function findById(id) {
return (
db('users')
.where({ id })
.first()
);
}
async function add(user) {
const [id] = await db('users').insert(user, 'id');
return findById(id);
}
Any help would be appreciated!

Node.js route sends to the incorrect controller

Problem: When I call this route /topproducts it enters in the orders_get_order function and not in the orders_most_ordered_products. This is very strange I can't understand why it enters in the wrong function.
The console error message:
message: 'Cast to ObjectId failed for value "topproducts" at path "_id" for model "Order"',
name: 'CastError',
stringValue: '"topproducts"',
kind: 'ObjectId',
value: 'topproducts',
path: '_id',
reason: undefined,
model: Model { Order }
My route.js
router.get("/topproducts", checkAuth, OrdersController.orders_most_ordered_products);
My controller order.js
exports.orders_most_ordered_products = async (req, res) => {
try{
let order = await order_service.get_most_ordered_products();
if ('error' in order){
res.status(order['status']).json(order)
}else{
res.status(200).json(order)
}
}catch(err){
console.log("most orders")
console.log(err)
//res.status(500).json(fatal_error_status);
}
};
//get uma order
exports.orders_get_order = async (req, res) => {
try{
let order = await order_service.get(req.params.orderId);
if ('error' in order){
res.status(order['status']).json(order)
}else{
res.status(200).json(order)
}
}catch(err){
console.log("get orders")
console.log(err)
//res.status(500).json(fatal_error_status);
}
};
And then it sends to the service order.js
exports.orders_most_ordered_products = async (req, res) => {
try{
let order = await order_service.get_most_ordered_products();
if ('error' in order){
res.status(order['status']).json(order)
}else{
res.status(200).json(order)
}
}catch(err){
console.log("most orders")
console.log(err)
}
};
exports.orders_get_order = async (req, res) => {
try{
let order = await order_service.get(req.params.orderId);
if ('error' in order){
res.status(order['status']).json(order)
}else{
res.status(200).json(order)
}
}catch(err){
console.log("get orders")
console.log(err)
}
};
The problem was the order of the routes, the route that takes an Id as a parameter was first so the server though I was calling that one.
router.get("/topproducts", checkAuth, OrdersController.orders_most_ordered_products);
router.get("/:orderId", checkAuth, OrdersController.orders_get_order);

ExpressJS/Mongoose Set variable to MongoDB result

I have a NodeJS app using Express and Mongoose. I am trying to write a POST route so that when a POST request to the URL is made (currently /api/v1/forms/:formId) then it will set the variable to the recipient`value from the MongoDB database. The :formID will match the _id in MongoDB.
So far I have:
app.post("/api/v1/forms/:formId", async (req, res) => {
//TODO: Create Mailer and email templates before finalising route.
const { _name, _email, _message } = req.body;
const form = Form({
name = req.body._name,
email = req.body._email,
message = req.body._message,
recipient = Form.findById(req.params.form_id, function(err, form) {
if (err)
res.send(err);
})
});
const mailer = new Mailer(form, contactFormTemplate(form));
try {
await mailer.send();
} catch (err) {
res.status(422).send(err);
}
});
I know this is not correct for the recipient field but this is what I could think of off the top of my head
Following what you did, you can do this:
app.post("/api/v1/forms/:formId", async (req, res) => {
//TODO: Create Mailer and email templates before finalising route.
const { _name, _email, _message } = req.body;
let recipient;
try{
recipient = await Form.findById(req.params.form_id).exec();
} catch(err){
res.status(422).send(err);
}
const form = Form({
name: req.body._name,
email: req.body._email,
message: req.body._message,
recipient: recipient
});
const mailer = new Mailer(form, contactFormTemplate(form));
try {
await mailer.send();
} catch (err) {
res.status(422).send(err);
}
});
The .exec() on mongoose return a promise, so it's possible to use async/await syntax.
As far as I understood the problem, here is what I could think of,
Form.findById(req.params.form_id, function(err, form) {
if (err){
res.send(err);
}else{
recipient = form.recipientValue;
}
});
Try this..
app.post("/api/v1/forms/:formId", async (req, res) => {
const { _name, _email, _message } = req.body;
let form;
try {
form = await Form.findById(req.params.formId).select('recipient').exec();
} catch (err) {
res.send(err);
return;
}
const nuForm = Form({
name = req.body._name,
email = req.body._email,
message = req.body._message,
recipient = form.recipient
});
try {
await new Mailer(nuForm, contactFromTemplate(nuForm)).send();
} catch(err) {
res.status(422).send(err);
}
}

Resources