I'm trying to build a messaging site using expressJS and reactJS. I'm currently working on the message-sending functionality of the site. My goal is that when the user clicks the send button on the react page, it makes a post request to the express backend, which pushes the message data to the correct user's "messages" array in my MongoDB Altlas collection. The problem is that I can't figure out how to push the message to the right user's array because it is nested with other data in the collection. Can anyone help me?
Here is my index.js express code that I'm stuck on:
const userSchema = new mongoose.Schema({
name: String,
password: String,
profilePicture: String,
contacts: [{name: String,
profilePicture: String,
messages: [{sender: String, message: String}],
}]
});
const User = new mongoose.model('User', userSchema);
app.post("/send-message", (req, res) => {
var sender = req.body.sender;
var recv = req.body.recv;
var message = req.body.msg
User.findOne({name: sender}).then(user => {
user.contacts.forEach(contact => {
if (contact.name === recv){
// newMessage is the object I would like to push to the
// messages array for the sender.
var newMessage = {sender: "me", message: message};
}
});
}).then(() => {
User.findOne({name: recv}).then(user => {
user.contacts.forEach(contact => {
if (contact.name === sender){
// newMessage is the object I would like to push to
// the messages array for the receiver.
var newMessage = {sender: "them", message: message};
}
});
}).then(res.send("Message Sent!"))
.catch(e => {console.log("2: " + e); res.send("Error!")});
}).catch(e => {console.log("1: " + e); res.send("Error!")});
});
Here is my React code for the SendMessage function:
const [usrn, setUsrn] = React.useState("<Username>");
const [pass, setPass] = React.useState("<Password>");
const [messageThread, setMessageThread] = React.useState({});
const [message, setMessage] = React.useState("");
const SendMessage = () => {
axios.post(props.apiUrl + "send-message/", {sender: usrn, recv:
messageThread.contact, msg: message})
.then(res => {
console.log(res.data);
setMessage("");
});
}
You can use findOneAndUpdate
to select the User
and $push
the new message object to the correct array. By selecting contacts.name
you can use the mongodb $
positional operator which will only push to the matching array element.
This example uses the async/await
pattern with try/catch
for easier error handling. You had multiple then.catch
blocks which is a downward spiral in my experience.
app.post("/send-message", async (req, res) => { //< Mark callback as async
try{
const sender = req.body.sender;
const recv = req.body.recv;
const message = req.body.msg
// Add message to sender
const userSender = await User.findOneAndUpdate({ //< use await pattern
name: sender,
'contacts.name': recv
},{
$push:{
'contacts.$.messages': { sender: "me", message: message }
}
}, {new: true});
if(!userSender){ //< If no sender was found return error
return res.status(400).json({
message: 'Sender not found'
})
}
// Add message to receiver
const userReceiver = await User.findOneAndUpdate({ //< use await pattern
name: recv,
'contacts.name': sender
},{
$push:{
'contacts.$.messages': { sender: "them", message: message }
}
}, {new: true});
if(!userReceiver){ //< If no receiver was found return error
return res.status(400).json({
message: 'Receiver not found'
})
// Handle deletion of object in sender
}
return res.status(201).json({
message: 'Messages Saved!'
})
}catch(err){
console.log(err);
return res.status(500).json({
message: 'Error on server.'
})
}
});