Problem inserting data into a nodejs and mongodb backend with reactjs frontend - javascript

I have an API running on nodejs with mongodb, and I am trying to insert data from the reactjs frontend.
This data comes from the user's Token and the id of a book that the user is interested in
Model:
let BookInterested = new Schema({
book: {
type: Schema.Types.ObjectId,
ref: "Book",
required: true
},
user: { type: Schema.Types.ObjectId, ref: "User", required: true },
timestamp: { type: Date, default: Date.now, required: true },
completed: { type: Boolean, default: false }
});
module.exports = mongoose.model("BookInterested", BookInterested);
The user and the book are coming from other models with are Book and User.
I have a Route with a simple post method:
Route:
router.post(
"/book/:bookId/interesteds",
auth.authenticate(),
(req, res) => {
meService.createInterested(req, res);
}
);
The Service has all the logic behind the route.
meService:
createInterested: function(req, res) {
let interested = {
user: req.user.id, //User Id from token
book: req.params.bookId, //book Id from route
};
let record = new BookInterested(interested);
record.save(err => {
if (err) return helper.error(res, "Error while creating " + err);
if (!record) return helper.error(res, "Record not found");
return res.json({ success: true });
});
},
In my frontend with is in reactjs I have the functions to fetch the books and the users
componentWillMount() {
this.retrieveBooks(this.props.params.id);
this.retrieveUser();
}
with is working just fine, my problem now is that I need a button where, when clicked, the user demonstrates that he or she has an interest in the book, like so:
<Button
onClick={() => this.setState({ open: !open, disabled: true })}
className="btn btn-big btn-tenho-interesse"
aria-expanded={open}
disabled={this.state.disabled}
>
Im Interested
</Button>
I create the handleclick function, but it is not working
handleclick:
handleClick = (req, res) => {
console.log(req);
Request.post(
`/me/books/${this.props.params.id}/interesteds`,
req,
res,
res => {
if (res.success) {
Alert.success(
"success"
);
} else
Alert.error(
"error"
);
}
);
};
I'm getting all sorts of errors, like "TypeError: Converting circular structure to JSON" and "res.json is not a function" and many others.
Any idea how I can do this?
Thanks.

Related

How do I send data to express backend from html frontend without a form?

I am trying to create a small ecommerce website where I need to have a cart page. I need to make it so when a user clicks on Add to cart button, the particular item will get sent to the backend and stored in the cart collection.
What I'm having trouble with is that when I click the add to card button and the function linked to it runs, I get an error stating : SyntaxError: Unexpected token " in JSON at position 0
Here's my function which is supposed to send data to the backend:
let addToCart = async (item) => {
// item is the object I get which contains all the information I need
try {
let product = item._id;
await fetch('http://localhost:5000/cart', {
method: 'post',
body: product,
headers: {
'Content-Type': 'application/json',
},
});
} catch (e) {
console.log({error : e});
}
};
This is my cart model :
const cartSchema = new mongoose.Schema(
{
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'users',
required: true,
},
cartProducts: {
type: Array
},
},
{
versionKey: false,
timestamps: true,
}
);
This is my cart controller POST CRUD
router.post('', authenticate, async (req, res) => {
const user = req.user;
console.log(user);`
try {
const cart = await Cart.findOneAndUpdate(
{ userId: user._id },
{
$push: {
cartProducts: req.body,
},
},
{ new: true, upsert: true }
);
return res.status(200).send(cart);
} catch (err) {
return res.status(400).send({ message: err.message });
}
});
I am able to get user using authenticate middleware.
I can post everything using Postman but I can't figure how to get rid of the Syntax error.
When I console.log typeof item, I get an object. Product's type is string. I have also tried using JSON.stringify but nothing seems to be working. I have also tried using bodyparser middleware.

How to sync data with MongoDb and know the status while a document is being created/updated

I'm developing a MERN chat app and trying to replicate the feature which WhatsApp provides -> message sent/seen status, so for that I need to check whether the message I/user sent is successfully synced/updated/created in my MongoDB, I know similar functionality can be achieved in AWS Amplify using the event outboxMutationEnqueued which says Dispatched when a local change has been newly staged for synchronization with the cloud, so it works like whenever we are trying to push something to synchronize with the cloud, this event is going to be fired, and once it is finished, outboxMutationProcessed is going to be triggered which says Dispatched when a local change has finished syncrhonization with the cloud and is updated locally.
So we can listen to these events whenever we are trying to send a message, and once our message mutation is processed we are going to receive outboxMutationProcessed, and then we can update the status of the message to sent or single tick or delivered.
import Amplify, {Hub} from 'aws-amplify';
useEffect(() => {
const listener = Hub.listen('datastore', async (hubData) => {
const {event, data} = hubData.payload;
if (event === 'networkStatus') {
console.log('User has a network connection: ', data.active);
}
if (event === 'outboxMutationProcessed') {
if (data.model === Message)
console.log('Mutation has been synced with the cloud: ', data);
// set the message status to delivered.
}
})
return () => listener();
}, []);
So, the question, do we have something similar in MongoDB? I'm currently using React Native, Node, Express, Sockets, Mongoose, MongoDB.
Currently, my API end point and collections (for creating a new message and saving in to db):
I have 3 collections: users, 'messages', 'chatRooms'.
const mongoose = require('mongoose');
const MessageSchema = mongoose.Schema({
roomId: String,
senderId: String,
text: String,
status: String, // INQUEUE, SENT, DELIVERED, READ
}, {
timestamps: true,
});
module.exports = mongoose.model('Message', MessageSchema);
router.post('/create_message', checkAuth, async (req, res) => {
const {chatRoomId, senderId, text} = req.body;
try {
const message = new Message({
chatRoomId,
senderId,
text,
});
const result = await message.save();
return res.status(200).json({
type: 'success',
data: result,
});
} catch (error) {
return res.status(422).send({error: `${error.message}`});
}
});
// SOCKET IMPLEMENTATION FOR REALTIME FEATURE
socket.on('listener', async data => {
io.to(id).emit('new_message', data);
const message = new Message({
chatRoomId: data.roomId,
senderId: data.senderId,
text: data.text,
status: data.status, // data.status = 'INQUEUE'
});
await message.save();
// maybe something here...? not sure
// data.status = 'SENT' after successful creation of document.
});
Maybe an event which we can fire, during the await message.save(...something here...), and if it is successfully saved in our DB, we can send it to our frontend or using socket?
If anyone could provide an example, it would be really helpful!
Edit: I changed this up a bit.
schemas:
const UserSchema = new mongoose.Schema({
name: String,
online: Boolean,
isActive: Boolean,
})
const RoomSchema = new mongoose.Schema({
name: String,
members: [{ type: mongoose.Schema.Types.ObjectId, ref: 'users' }],
})
const MessageSchema = new mongoose.Schema({
roomId: { type: mongoose.Schema.Types.ObjectId, ref: 'rooms' },
senderId: { type: mongoose.Schema.Types.ObjectId, ref: 'users' },
text: String,
deliveredTo: [{ user: { type: mongoose.Schema.Types.ObjectId, ref: 'users' }, timestamp: Date }],
readBy: [{ user: { type: mongoose.Schema.Types.ObjectId, ref: 'users' }, timestamp: Date }],
})
app.js
Mongo.connect(uri, (err, client) => {
mongoose.connect(uri)
socketClient.on('connection', socket => {
//
//on load all users for login select
User.find().then(users => socket.emit('users', users))
....
//
//setting pipeline for readStream to only watch for changes in the messages collection
//where the roomId field is the current room the user has loaded
//setting fulldocument : 'updateLookup' option will always return the fulldocuemnt with any updates
//without this, the fulldocument will only be returned on our insert change
const pipeline = [{ $match: { 'fullDocument.roomId': ObjectId(room.id) } }]
const collection = client.db('chat').collection('messages')
const messageStream = collection.watch(pipeline, { fullDocument: 'updateLookup' })
messageStream.on('change', async function (change) {
const updates = change.updateDescription?.updatedFields
//
//if a new document is created in messages, then push that message to the client
if (change.operationType.match(/insert/i)) {
const doc = await Message.findById(change.fullDocument._id).populate(
'deliveredTo.user readBy.user'
)
const status = await getStatus(doc, room.id)
socket.emit('push message', { doc, status: status })
//
//if delivered to has been updated on a message then send to client
} else if (updates?.deliveredTo) {
const status = await getStatus(change.fullDocument, room.id)
socket.emit('status change', {
id: change.fullDocument._id,
status: status,
user: change.fullDocument.senderId,
})
} else if (updates?.readBy) {
//
//if readby has been updated on a message then send to client
const status = await getStatus(change.fullDocument, room.id)
socket.emit('status change', {
id: change.fullDocument._id,
status: status,
user: change.fullDocument.senderId,
})
}
})
...
})
})

How to check if there are no more documents to update using findOneAndUpdate

So I am learning CRUD for a school project and I followed a tutorial that was really useful. However, when I completed it I noticed that when there are no more quotes to update, it still updates quotes. How can I change this so that it will stop updating quotes that arent even there?
app.put('/quotes', (req, res) => {
quoteCollection.findOneAndUpdate(
{ name: 'Yoda' },
{
$set: {
name: req.body.name,
quote: req.body.quote
}
},
{upsert: true}
)
.then(result => {
//The if block that i am trying
if (result.deletedCount === 0) {
return res.json('No quote to delete')
}
})
.catch(error => console.error(error))
})
Why are you passing {name: "Yoda}? This route is supposed to only update the quote with "Yoda" as its name? If not, then you need to grab from the request object the quote that should be updated.
I tried to create a different version, based on the assumption that the quote that should be updated will come from the req.body:
app.put("/quotes", async (req, res) => {
//Grab the name/id/identifier for the quote you want to update from the body
const query = req.body.name;
// Try to update the document on the database
try {
const result = await quoteCollection.findOneAndUpdate(
query,
{
name: req.body.name,
quote: req.body.quote,
},
{
upsert: true,
new: true,
}
);
// If it worked, it will return the updated quote
res.status(200).json({
status: 200,
data: {
result,
},
});
} catch (err) {
res.status(400).json({
status: 400,
message: "Something went wrong",
});
}
});

Node JS preventin users from deleting other user's products

I have a REST API built with Node JS and I'm currently using MongoDB as my database. I want to prevent the users from deleting another user's products and for this I checked if the userId from the decoded token is the same as the product userId.
Product schema
const mongoose = require("mongoose");
const productSchema = mongoose.Schema(
{
_id: mongoose.Schema.Types.ObjectId,
userId: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true },
price: { type: Number, required: true },
productImage: { type: String, required: false },
category: {
type: mongoose.Schema.Types.ObjectId,
ref: "Category",
required: true
},
gender: { type: String, required: true }
},
{ timestamps: { createdAt: "created_at" } }
);
module.exports = mongoose.model("Product", productSchema);
The delete product method:
const id = req.params.productId;
Product.findById({ _id: id }).then((product) => {
if (product.userId != req.user._id) {
return res.status(401).json("Not authorized");
} else {
Product.deleteOne({ _id: id })
.exec()
.then(() => {
return res.status(200).json({
message: "Product deleted succesfully",
});
})
.catch((err) => {
console.log(err);
return res.status(500).json({
error: err,
});
});
}
});
};
As you guys see first I'm searching executing the findByID method to access the userId property of the product, then I'm comparing the userId from the response with the userId from the decoded token.
I don't think my method is very efficient since it's running both findById and deleteOne methods.
Can you help me with finding a better solution for this?
as Guy Incognito mentioned, what you are trying to do is an OK thing and you may want to keep it this way in case you want to send a 404 status stating the product they are trying to remove does not exist.
however, if you are trying to do it with only one request
Product.deleteOne({ _id: id, userId: req.user._id })
hope it helps!

Node.js RESTful API validation error on array schema

I am creating a RESTful API using Node.js and mongoose by following the tutorial by Acedemind. I have got it working just fine and am now expanding it to allow the client to post several products in the same order. Basically I am modifying a simple "POST" request to be an array instead of some variables. My problem is that I run into a long validation error that hinders the array from being created. Here is the code for the post request:
router.post("/", async (req, res, next) => {
const productsMaybeFalses = await Promise.all(req.body.products.map(async ({ productId })=> {
const product = await Product.findById(productId);
if (!product) {
return false;
}
return {
...product,
productId,
}
}));
const errors = productsMaybeFalses
.map((productMaybeFalse, index) => {
return {
productMaybeFalse, index
}
})
.filter(({ productMaybeFalse }) => !productMaybeFalse)
if (errors.length) {
console.log(error);
return;
}
console.log(productsMaybeFalses);
const products = productsMaybeFalses
.filter((productMaybeFalse) => productMaybeFalse);
const order = new Order({
_id: mongoose.Types.ObjectId(),
products: products
});
return order.save().then(results => {
console.log(results);
res.status(201).json(results.map((result) => ({
message: "order stored",
createdOrder: {
_id: result._id
},
request: {
type: "GET",
url: "http://localhost:3000/orders/" + result._id
}
})));
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
})
})
})
And here is the Schema for the Order:
const mongoose = require("mongoose");
const pSchema = mongoose.Schema({
productId: { type: mongoose.Schema.Types.ObjectId, ref: "Product", required: true},
quantity: { type: Number, default: 1}
});
const orderSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
products: [pSchema]
});
module.exports = mongoose.model("Order", orderSchema)
To keep this question from being too long I will only post the end part of the error. The part that I feel tells the most information abut the problem. If anyone wants the whole error message to better understand the problem and maybe come up with a solution for me I will be very willing to post it as well. Here it is:
kind: 'Array',
value: [Array],
path: 'products',
reason: TypeError: value[i].toObject is not a function
at DocumentArray.cast (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/schema/documentarray.js:309:27)
at DocumentArray.SchemaType.applySetters (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/schematype.js:755:12)
at model.$set (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/document.js:922:18)
at model._handleIndex (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/document.js:740:14)
at model.$set (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/document.js:697:22)
at model.Document (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/document.js:114:12)
at model.Model (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/model.js:73:12)
at new model (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/model.js:4324:13)
at router.post (/Users/axelhagman/Documents/Jacobs/api/routes/orders.js:70:17)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:189:7) } },
_message: 'Order validation failed',
name: 'ValidationError' }
POST /orders/ 500 440.085 ms - 7622
I am very new to using node.js and creating API overall so any help would be very much appreciated. Thanks!

Categories