MERN Stack: Saving Form Data to mLab - Formatting Issue - forms

I am getting the data from my form to my mLab but it is formatted in a way i don't like. it comes though like this
{ '{"email":"ben#benjamin.com","message":"not what i want"}': '' }
and even worse it saves as this
"data": "{\"{\\\"email\\\":\\\"ben#benjamin.com\\\",\\\"message\\\":\\\"this is ridiculous\\\"}\":\"\"}"
so it is setting my data as the key to nothing basically. i resorted to changing my Schema to an Object just so i can save something. ideally i want a key for each piece of data. and i want to be able to access it later. i couldn't get body parser to work so i am using express-formidable as middleware. there are alot of moving parts and if i change things around i crash the server and get error no matter what i do. okay here is some of the code from both sides.
SERVER CODE:
var formSchema = new Schema({
data: Object
})
app.use(formidable());
app.post('/contact', function(req,res,next){
console.log(req.fields)
var item = JSON.stringify(req.fields)
var form = new Form({data: item
}).save(function(err,data){
if(err) throw err
if(data) res.json(data)
})
})
CLIENT SIDE CODE
submitData(e){
e.preventDefault();
let email = this.state.value;
let msg = this.state.value2;
let data = {
email: email,
message: msg
}
fetch('/contact', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: JSON.stringify(data)
})
.then((response) => response.json())
.then((responseData) => {
console.log("Response:",responseData);
this.setState({
value: "",
value2: "",
status: "THANKS FOR CONTACTING ME I'LL BE IN TOUCH SOON"
})
}).catch((error) => {
console.log(error)
})
}
this is driving me insane. ideally i would like to do it right. or somehow access the object and get at the email and message keys and just change my Schema. then i could assign the keys in my new Form(... section

In a bizarre twist of events i solved it sort of. this can't be the best way but it works. i would still like to know where along the line i went wrong. i saw a tutorial where the form data was easily saved...granted in wasn't from a react front end...anyway this is what i did
My Custom Solution
var formSchema = new Schema({
email: String,
message: String
})
app.use(urlencodedParser);
app.post('/contact', function(req,res,next){
var item = Object.keys(req.body)
var o = JSON.parse(item[0])
var e = o.email
var m = o.message
console.log(e)
//var item = JSON.stringify(req.fields)
var form = new Form({email: e,message: m
}).save(function(err,data){
if(err) throw err
if(data) res.json(data)
})
})
i switched back to body-parser from formidable but either way i was getting that weird object
i still don't know why my data object was {'{'key':'value'}':''} i thought my problem was on the server side...but maybe it is something with how i send the POST. someone smarter and more experienced isnt hard to find :)

Related

dealing with promises returned from mongoose

This is probably a simple concept to some but an understanding of promises is something that I continue to struggle with...
I have a simple web app that connects to a mongo DB using mongoose and an MVC pattern. I used this tutorial to get up and running but I am not looking to build an API just a back end for my application. I believe interfacing with the controller is where I need some help understanding...
My App structure is as follows
db.congif.js - DB connection managed here
movie.model.js - this is where my object schema is defined
movie.contrller.js - this is where the db operations are written
index.js - main file for my app
Here is the example controller
exports.create = (req, res) => {
// Validate request
if (!req.body.title) {
res.status(400).send({ message: "Content can not be empty!" });
return;
}
// Create a Tutorial
const tutorial = new Tutorial({
title: req.body.title,
description: req.body.description,
published: req.body.published ? req.body.published : false
});
// Save Tutorial in the database
tutorial
.save(tutorial)
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while creating the Tutorial."
});
});
};
I want to update this to accept an object and return a "response" here begins my lack of understanding
exports.create = (tutorialObject) => {
// Save Tutorial in the database
tutorial
.save(tutorialObject)
.then(data => {
return data;
})
.catch(err => {
return {
message:
err.message || "Some error occurred while creating the Tutorial."
};
});
};
and finally how I am calling this
let dbResponse = tutorial.create(
{
original_title : original_title,
title : title,
poster_path : poster_path
})
So my question... Please help me understand the correct way to code\call this. Is the controller code correct, am I calling it correctly? I am not sure because even though this is working to write records to the DB the dbResponse is always undefined.
The code works to write records to the DB but the response from the controller is undefined. I would expect to have the response be the record that was inserted?

UnhandledPromiseRejectionWarning: MongoError: w has to be a number or a string at Connection

Anyone has an idea why I am getting this error : " UnhandledPromiseRejectionWarning: MongoError: w has to be a number or a string at Connection." ? I got this error while I was running the code below. It's purpose is to check if a user is in a mongodb database if not it creates a new user by the user email and the hash password.
I don't know if there is any relation, the code seem to work well, but when I updated my mac to catalina OS yesterday I started having this issue.
routerAuth.post('/signup', (req, res, next) => {
const result = Joi.validate(req.body, schema)
if (result.error === null) {
Profile.findOne({
email: req.body.email
}).then(profile => {
if (profile) {
const error = new Error(
'The email is already in use. Please choose another one'
)
res.status(409)
next(error)
} else {
bcrypt.hash(req.body.password.trim(), 12).then(hashedpassword => {
let newProfile = new Profile({
first: req.body.first,
last: req.body.last,
password: hashedpassword,
email: req.body.email
})
Profile.insertMany(newProfile).then(profile => {
res.json(profile)
})
})
}
})
}
})
I'm not sure where this error comes from (I found a pull request from 2016, which allegedly fixed this issue), but when I changed the url parameter order, I didn't get this anymore.
So instead of this:
/<database>?retryWrites=true&w=majority
write this:
/<database>?w=majority&retryWrites=true
I had the same issue. I'm not sure why but on my case, when I removed the "&w=majority" portion from the MongoDB connection string it worked.

Sign Up not working and throwing params errors

I am currently able to sign in just fine with previously created user credentials and use the app as normal, but am unable to create a new user. I am using React.js on the client side and an Express api on the backend. I am getting a mongoose validation error. All of the authentication came with the template the course has us use and I haven't touched any of those files. I even went back and compared commit history trees to ensure that nothing was changed.
Here is my user schema and sign-up route. I tried eliminating uniqueness from the model and that didn't impact it. I know there is a lot of potential places something could be going wrong, but if anyone has any suggestions on potential issues I would be forever grateful! I console logged the req.body.credentials within sign up and the data being sent over looks good.
Error code: 422 Unprocessable Entity
Server side error: 'The received params failed a Mongoose validation'
const mongoose = require('mongoose')
const { petSchema } = require('./pet.js')
const { pictureSchema } = require('./picture.js')
const userSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true
},
hashedPassword: {
type: String,
required: true
},
token: String,
pets: [petSchema],
pictures: [pictureSchema]
}, {
timestamps: true,
toObject: {
// remove `hashedPassword` field when we call `.toObject`
transform: (_doc, user) => {
delete user.hashedPassword
return user
}
}
})
module.exports = mongoose.model('User', userSchema)
// SIGN UP
// POST /sign-up
router.post('/sign-up', (req, res) => {
// start a promise chain, so that any errors will pass to `handle`
console.log(req.body.credentials)
Promise.resolve(req.body.credentials)
// reject any requests where `credentials.password` is not present, or where
// the password is an empty string
.then(credentials => {
if (!credentials ||
!credentials.password ||
credentials.password !== credentials.password_confirmation) {
throw new BadParamsError()
}
})
// generate a hash from the provided password, returning a promise
.then(() => bcrypt.hash(req.body.credentials.password, bcryptSaltRounds))
.then(hash => {
// return necessary params to create a user
return {
email: req.body.credentials.email,
hashedPassword: hash
}
})
// create user with provided email and hashed password
.then(user => User.create(user))
// send the new user object back with status 201, but `hashedPassword`
// won't be sent because of the `transform` in the User model
.then(user => res.status(201).json({ user: user.toObject() }))
// pass any errors along to the error handler
.catch(err => handle(err, res))
})
Solved. One of the subdocuments I had within user had a key with a value set to unique. I needed to eliminate that because my database was indexing users with a null value and throwing a duplicate error. I then needed to reset my database (I just renamed it to test it out) so that it didn't have any saved indexes with that configuration. I just deleted my collections within Heroku as well (luckily I didn't have significant amounts of data in there and this solution was perfectly fine for my situation). I am now able to sign up users again without any duplicate key errors.

Handling nested callbacks/promises with Mongoose

I am a beginner with Node.js and Mongoose. I spent an entire day trying to resolve an issue by scouring through SO, but I just could not find the right solution. Basically, I am using the retrieved values from one collection to query another. In order to do this, I am iterating through a loop of the previously retrieved results.
With the iteration, I am able to populate the results that I need. Unfortunately, the area where I am having an issue is that the response is being sent back before the required information is gathered in the array. I understand that this can be handled by callbacks/promises. I tried numerous ways, but I just haven't been successful with my attempts. I am now trying to make use of the Q library to facilitate the callbacks. I'd really appreciate some insight. Here's a snippet of the portion where I'm currently stuck:
var length = Object.keys(purchasesArray).length;
var jsonArray = [];
var getProductDetails = function () {
var deferred = Q.defer();
for (var i = 0; i < length; i++) {
var property = Object.keys(purchasesArray)[i];
if (purchasesArray.hasOwnProperty(property)) {
var productID = property;
var productQuery = Product.find({asin:
productQuery.exec(function (err, productList) {
jsonArray.push({"productName": productList[0].productName,
"quantity": purchasesArray[productID]});
});
}
}
return deferred.promise;
};
getProductDetails().then(function sendResponse() {
console.log(jsonArray);
response = {
"message": "The action was successful",
"products": jsonArray
};
res.send(response);
return;
}).fail(function (err) {
console.log(err);
})
});
I am particularly able to send one of the two objects in the jsonArray array as the response is being sent after the first element.
Update
Thanks to Roamer-1888 's answer, I have been able to construct a valid JSON response without having to worry about the error of setting headers after sending a response.
Basically, in the getProductDetails() function, I am trying to retrieve product names from the Mongoose query while mapping the quantity for each of the items in purchasesArray. From the function, eventually, I would like to form the following response:
response = {
"message": "The action was successful",
"products": jsonArray
};
where, jsonArray would be in the following form from getProductDetails :
jsonArray.push({
"productName": products[index].productName,
"quantity": purchasesArray[productID]
});
On the assumption that purchasesArray is the result of an earlier query, it would appear that you are trying to :
query your database once per purchasesArray item,
form an array of objects, each containing data derived from the query AND the original purchasesArray item.
If so, and with few other guesses, then the following pattern should do the job :
var getProductDetails = function() {
// map purchasesArray to an array of promises
var promises = purchasesArray.map(function(item) {
return Product.findOne({
asin: item.productID // some property of the desired item
}).exec()
.then(function product {
// Here you can freely compose an object comprising data from :
// * the synchronously derived `item` (an element of 'purchasesArray`)
// * the asynchronously derived `product` (from database).
// `item` is still available thanks to "closure".
// For example :
return {
'productName': product.name,
'quantity': item.quantity,
'unitPrice': product.unitPrice
};
})
// Here, by catching, no individual error will cause the whole response to fail.
.then(null, (err) => null);
});
return Promise.all(promises); // return a promise that settles when all `promises` are fulfilled or any one of them fails.
};
getProductDetails().then(results => {
console.log(results); // `results` is an array of the objects composed in getProductDetails(), with properties 'productName', 'quantity' etc.
res.json({
'message': "The action was successful",
'products': results
});
}).catch(err => {
console.log(err);
res.sendStatus(500); // or similar
});
Your final code will differ in detail, particularly in the composition of the composed object. Don't rely on my guesses.

Mongoose findByIdAndUpdate not working

I have a fairly straight forward method below to update a document based on its ObjectId. It does not return an error but it fails to make the required updates to the document. I think it is failing because, according to my research, findByIdAndUpdate() takes only plain Javascript whereas job._id is an ObjectId from the document that I want to update. Can someone tell me how to make this work correctly?
function handleEncoderResponse(xmlResponse, job) {
var r = et.parse(xmlResponse);
var mediaID = r.findtext('./MediaID');
var message = r.findtext('./message');
EncodingJob = mongoose.model('EncodingJob');
EncodingJob.findByIdAndUpdate( job._id, {
"MediaID": mediaID,
"Status": message
}, function(err, result) {
if (err) console.log(err);
console.log(result);
});
}
Edit: Per this question Mongoose update document Fail with findByIdAndUpdate
I also tried the following code to no avail.
job.MediaID = mediaID;
job.Status = message;
job.save(function(err, res) {
if(err) console.log(err);
});
This approach yields the issue. It does not update the document and it does not return an error.
As it turns out, my mistake was forgetting to define MediaID and Status in the Schema as follows:
var encodingJobSchema = new mongoose.Schema({
...
MediaID: String,
Status: String
});