Changing the collection based on the request type - express

This controller accepts the form and updates the data.
export const createPost = async (req, res) => {
const { title, message, selectedFile, creator, tags } = req.body;
const newPostMessage = new OrangeModel ({ title, message, selectedFile, creator, tags })
try {
await newPostMessage.save();
res.status(201).json(newPostMessage );
} catch (err) {
res.status(409).json({ message: err.message });
}
}
I want to change the collection type based on the request.
when the request is from the Grapes url, the model(or collection) should change to GrapeModel from OrangeModel. How to do this?

If you want a POST /Grapes to be behave differently from a POST /Oranges, you can attach your controller to both paths and evaluate the path inside your code.
const createPost = async (req, res) => {
let newPostMessage;
if (req.path === "/Oranges") newPostMessage = new OrangeModel(...);
else if (req.path === "/Grapes") newPostMessage = new GrapeModel(...);
try {
await newPostMessage.save();
...
};
app.post(["/Oranges", "/Grapes"], createPost);

Also I got the answer like this:
exports.createPost =Model=> async (req, res) => {
try {
const doc = await Model.create(req.body, {
new: true,
runValidators: true,
});
res.status(200).json({
status: 'success',
data: {
doc,
},
});
} catch (error) {
res.status(400).json({
status: 'fail',
message: error,
});
}
};
Here just call createPost function with the model name

Related

express crashes if req.file is undefined

when making a post request to an express api without an attached file in the request, the api crashes and provides the TypeError: Cannot read properties of undefined (reading 'filename') error. However i would like to make it so the api does not crash when a post request is made without an attached image. any ideas ?
express code :
const storage = multer.diskStorage({
destination: (req, res, cb) => {
cb(null, dir)
},
filename: (req, file, cb) => {
cb(null, Date.now() + file.originalname)
}
})
const upload = multer({
storage: storage
})
router.get('/', async (req, res) => {
try {
const members = await Member.find();
res.json(members);
} catch (err) {
res.status(500).json({ message: err.message });
}
})
router.get('/:id', getMember, async (req, res) => {
res.json(res.member)
})
router.post('/', upload.single('image'), async (req, res) =>{
const member = new Member({
name: req.body.name,
occupation: req.body.occupation,
bio: req.body.bio,
join: req.body.join,
image: req.file.filename
})
try {
const newMember = await member.save()
res.status(201).json(newMember)
} catch (err) {
res.status(400).json({ message: err.message });
}
})
nextjs code to actually send the file:
const submitHandler = (e) => {
e.preventDefault()
const formDatas = new FormData()
formDatas.append('name', name)
formDatas.append('occupation', occupation)
formDatas.append('bio', paragraph)
formDatas.append('join', date)
formDatas.append('image', img)
console.log(formDatas)
axios
.post(api + '/members', formDatas)
.then(res => console.log(res))
.catch(err => console.log(err))
}
Error occurred because you're trying to access object of undefined variable req.file;
You can make changes according to your need
1 If you don't want to accept request without any file
router.post('/', upload.single('image'), async (req, res) => {
if (!req.file) { //or you can check if(req.file===undefiend)
return res.status(400).json({ message: 'Please attach a file' });
}
const member = new Member({
name: req.body.name,
occupation: req.body.occupation,
bio: req.body.bio,
join: req.body.join,
image: req.file.filename
})
try {
const newMember = await member.save()
res.status(201).json(newMember)
} catch (err) {
res.status(400).json({ message: err.message });
}
})
2 If you want to store null/empty string (in case of no file upload)
router.post('/', upload.single('image'), async (req, res) => {
const member = new Member({
name: req.body.name,
occupation: req.body.occupation,
bio: req.body.bio,
join: req.body.join,
image: req.file!==undefined ? req.file.filename : null
})
try {
const newMember = await member.save()
res.status(201).json(newMember)
} catch (err) {
res.status(400).json({ message: err.message });
}
})
Change as shown below
const member = new Member({
name: req.body.name,
occupation: req.body.occupation,
bio: req.body.bio,
join: req.body.join,
image: req.file.filename ? req.file.filename : ""
})
This should stop crashing but until I see the whole code wont know how you are handling error

Variables not registering in await function

What I'm trying to accomplish here is use req.body to pass the query arguments and when it hits the endpoint, the variables should be used in the create function of the Notion API. Unfortunately, the variables are registering as undefined. I figure that it's a scope issue, but I can't figure out how to structure this.
export default async function handler(req, res) {
const {
companyName,
email,
panelNames,
tags,
markers,
inquiry
} = req.body;
try {
await notion.pages
.create({
// use variables here
companyName: companyName //says undefined
}).then((result) => {
res.
})
} catch (e) {
console.log(e);
}
}
//Frontend code
const bodyQuery = {
companyName: "Example",
email: "example#gmail.com",
...
};
try {
await fetch("/api/v1/submit", headers)
.then((res) => {
return res.json();
})
.then((res) => {
setTests(res);
});
} catch (e) {
console.log("e:", e);
}
Because you did not pass bodyRequest to the request, so your backend can not receive data. Here's an example how to do it:
await fetch('/api/v1/submit', {
method: 'POST',
headers,
body: JSON.stringify(bodyRequest)
})
.then((res) => {
return res.json();
})
.then((res) => {
setTests(res);
});
And make sure your REST endpoint is POST. Hope it help!

mongoose save by reference multiple document

I'd like to make new document by reference of two documents.
**app.post('/student_badge/register', async (req, res) => {
const name = req.body.name;
const category = req.body.category;
People.find({name: name}, '_id', function (err, doc) {
if (err) return handleError(err);
var obj = eval(doc);
id = obj[0]._id;
})
Badge.find({category: category}, 'points title', function (err, doc) {
if (err) return handleError(err);
var obj2 = eval(doc);
points = obj2[0].points;
title = obj2[0].title;
console.log(title + " " + points);
});
data = {
id: id,
title: title,
points: points
}
console.log("data: " + data);
const sbadge = new StudentBadge(data);
sbadge.
save()
.then(result => {
res.status(201).json({
message: 'Post created successfully!',
post: result
});
})
.catch(err => {
console.log(err);
});
});**
But I cannot call three variables like id, title, points to store them in 'data'.
How can I call variables?
Thanks
Your code does not work because the variables you are trying to access, i.e. id, title, points, are being set on a callback function that gets executed asynchronously.
I would suggest using async/await instead of callbacks so that you can thereafter use the data from the other documents you are querying in the same function. In addition, I suggest to use findOne() since you only access the first entry in db.
Something like the example below should work: (I have abstracted the middleware in a separate function for clarity to use with express)
const createStudentBadge = async (req, res, next) => {
const {name, category} = req.body;
let person, badge;
try {
person = await Person.findOne({name}); // shortcut for {name: name}
badge = await Badge.findOne({category});
} catch(err) {
// handle error
}
if (!person || !badge) {
// Handle case where no document has been found in db
// This case will not throw an error when calling find()
}
data = {
id: person._id,
title: badge.title,
points: badge.points
}
const studentBadge = new StudentBadge(data);
try {
await studentBadge.save();
} catch(err) {
// handle error
}
res.status(201).json({
message: 'Post created successfully!',
post: studentBadge
});
}
app.post('/student_badge/register', createStudentBadge);
If you wanted to perform the querying in parallel, you could make use of Promise.all() and run both queries at the same time. More info can be found at MDN documentation

Sqlite3 returning empty array with GET request in Express

I am trying to make a get request to an sqlite3 table, using Express, based on input from a form. The fetch request works and so does the db.all, but I receive a response as an empty array from rows. I tried req.query and req.params already. Not sure where the error is.
//server.js
app.get('/names/state', (req, res, next) => {
const stateValue = req.query.state;
db.all(`SELECT name FROM states WHERE name=$stateVal`,
{
$stateVal: stateValue
},
(err, rows) => {
res.send({rows:rows});
})
});
//script.js
const fetchOneBtn = (e) => {
e.preventDefault();
const stateVal = stateInputValue.value;
fetch(`/names/state?state=${stateVal}`)
.then(response =>{
if(response.ok){
return response.json();
}
}).then(names => {
console.log(names);
})
};
You can change your code in your backend with this code below:
app.get('/names/state', (req, res, next) => {
const stateValue = req.query.state;
var query = "SELECT name FROM states WHERE name = " + stateValue;
db.all(query, (err, rows) => {
if(err) {
console.log(err);
res.status(500).send(err);
}else {
res.send({rows});
}
})
});
Now, for your frontend, you can change with the code below:
const fetchOneBtn = async (e) => {
e.preventDefault();
const stateVal = stateInputValue.value;
try {
const response = await fetch(`/names/state?state=${stateVal}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
});
console.log(await response.json());
return await response.json();
} catch(ex) {
console.log(ex);
}
};
I hope it can help you.

validating with expressjs inside (res, req) function instead of inside a middleware

I'm using express-validator library to validate on the backend. This is the library:
https://express-validator.github.io/docs/index.html
I have this code
// ...rest of the initial code omitted for simplicity.
const { check, validationResult } = require('express-validator');
app.post('/user', [
check('username').isEmail(),
check('password').isLength({ min: 5 })
], (req, res) => {
// Finds the validation errors in this request and wraps them in an object with handy functions
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
User.create({
username: req.body.username,
password: req.body.password
}).then(user => res.json(user));
});
Is it possible to call validation function inside the (req, res) =>{ }
I have some requirements where I need to check what has arrived in via request before I can construct the validation array.
Basically I would like to be able to do this:
app.post('/user', (req, res) => {
const { importantParam, email, password, firstname, lastname } = request.body
let validateThis = []
if(importantParam == true)
validateThis = [
check('username').isEmail(),
check('password').isLength({ min: 5 })
]
else
validateThis = [
check('username').isEmail(),
check('password').isLength({ min: 5 })
check('firstname').isLength({ min: 5 })
check('lastname').isLength({ min: 5 })
]
runValidationFunction(validateThis)
//now below code can check for validation errors
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
User.create({
username: req.body.username,
password: req.body.password
}).then(user => res.json(user));
});
This is what I need to do, construct the validation array based on if one of the params has a specific value. I can't figure out how to do that with the first example since it seems request is not possible to access when taking this approach
app.post('/user', validationArray, (req, res) => {}
Any ideas how I can call express-validate validate function directly inside
(req, res) => {}
What you can do is to run a check inside a custom validation. You should require validator as well.
const { check, body, param } = require('express-validator/check');
const { validator } = require('express-validator');
const checkValidation = () => {
return [
check('importantParam')
.custom((value, {req}) => {
const data = req.body;
if (!validator.isEmail(data.username)) throw new Error("Not valid Email");
if (!validator.isLength(data.password, {min:5})) throw new Error("Not valid password");
// if importantParam is false, add the following properties to validation
if (!value) {
if (!validator.isLength(data.firstname, {min:5})) throw new Error("Not valid firstname");
if (!validator.isLength(data.lastname, {min:5})) throw new Error("Not valid lastname");
}
return true;
})
];
};
app.post('/user', checkValidation(), (req, res) => {
//now below code can check for validation errors
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
// do stuff here
});
Hope it helps!