PUT request from React to mongodb with Axios - node.js

I need some little help. PUT request doesn't work when I send it from React app using Axios. But when I test PUT api from Postman - it works as it suppose to. Server side - node+mongoose+mongodb.
modifyCurrentHaspInfo = (e) => {
if (prompt("Enter password:") === "123456") {
axios.put("/hasp/change", {
data: {
_id: "5cd40f6f1e922c236caa82f4",
serial: "11111-22222",
soft: "test-put"
}
})
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
})
} else {
alert("Wrong password!");
}
}
When it finds correct id - data in body must be changed. Here is code from server:
//PUT request
app.put("/hasp/change", function(req, res) {
//console.log(req.body);
HaspInfo.findOneAndUpdate({_id: req.body._id}, {$set:req.body}, {new: true}, function(err, hasps) {
if (err) {
res.status(500).send({error: "Could not modify hasp info..."});
} else {
//console.log(hasps);
res.status(200).send(hasps);
}
});
});

It's because your axios is malformed (not what you're expecting at the backend).
The way you're sending data now from axios can be accessed at the backend as
req.body.data
// which will be an object like
{
_id: "5cd40f6f1e922c236caa82f4",
serial: "11111-22222",
soft: "test-put"
}
So _id can be accessed like req.body.data._id. Change your request to one of the following (note the differences)
axios.put("/hasp/change", {
_id: "5cd40f6f1e922c236caa82f4",
serial: "11111-22222",
soft: "test-put"
})
Or
axios.put({
url: "/hasp/change",
data: {
_id: "5cd40f6f1e922c236caa82f4",
serial: "11111-22222",
soft: "test-put"
}
})

Related

No able to send array from front end (reactJs) to the backend (nodejs)

This is the code for on my front end
When the object vendor is sent to the backend, the array key, value pair businessImages is empty [I came to this conclusion when nothing got console logged when I tried logging businessImages in the backend]
When I console log the vendor object, from the front end, it clearly shows that businessImages is populated. I have no idea what's going on.
Frontend code (reactjs)
const handleSubmit = async (e) => {
e.preventDefault();
if (businessImages.length > 15) {
alert("You've selected too many images for your business");
setBusinessImages([]);
} else {
const formDataBusinessImages = new FormData();
const formDataProfilePicture = new FormData();
vendor = {
name: name.current.value,
email: email.current.value,
mobileNumber: mobileNumber.current.value,
businessAddress: businessAddress.current.value,
mainService: selectedService,
subServive: selectedSubService,
businessImages: [],
aboutBusiness: aboutBusiness.current.value,
minPrice: minPrice.current.value,
maxPrice: maxPrice.current.value,
featured: featured,
};
formDataProfilePicture.append("file", profilePicture);
formDataProfilePicture.append(
"upload_preset",
process.env.REACT_APP_CLOUDINARY_KEY
);
try {
await axios
.post(
"https://api.cloudinary.com/v1_1/devign/image/upload",
formDataProfilePicture
)
.then((res) => {
console.log("Profile pic uploaded");
vendor.profilePicture = res.data.secure_url;
});
} catch (err) {
console.log(err);
}
businessImages.map(async (image) => {
formDataBusinessImages.append("file", image);
formDataBusinessImages.append("upload_preset", process.env.REACT_APP_CLOUDINARY_KEY);
try {
await axios
.post(
"https://api.cloudinary.com/v1_1/devign/image/upload",
formDataBusinessImages
)
.then((res) => {
vendor.businessImages.push(res.data.secure_url);
});
} catch (err) {
console.log(err);
}
});
handleUpload(vendor);
}
};
const handleUpload = async (uploadData) => {
try {
// console.log(businessImagesUrl);
// console.log(uploadData);
await axios.post("/auth/register", uploadData);
} catch (err) {
console.log(err);
}
};
Backend code (nodejs)
router.post("/register", async (req, res) => {
const newVendor = new Vendor({
name: req.body.name,
email: req.body.email,
mobileNumber: req.body.mobileNumber,
businessAddress: req.body.businessAddress,
mainService: req.body.mainService,
subService: req.body.subService,
profilePicture: req.body.profilePicture,
businessImages: [],
minPrice: req.body.minPrice,
maxPrice: req.body.maxPrice,
featured: req.body.featured,
});
try {
newVendor.businessImages.push(req.body.businessImages);
console.log(req.body.businessImages, req.body.profilePicture);
const vendor = await newVendor.save();
res.status(200).json("Successfully registered");
} catch (err) {
console.log(err);
}
});

connecting react frontend with node js rest api

I'm connecting my react frontend with a rest API from my node js backend folder but once the post request is send i keep getting this
errors
const handleSubmitData = async (event) => {
try {
const { user } = props;
const updatedData = (data) => {
props.updatedUserData(data);
};
await axios.post(`${BASE_API_URL}/api/auth/register`, { ...user, ...updatedData });
Swal.fire('Awesome!', "You're successfully registered!", 'success').then(
(result) => {
if (result.isConfirmed || result.isDismissed) {
props.resetUser();
navigate('/user/login');
}
}
);
} catch (error) {
console.log(error)
if (error.response) {
Swal.fire({
icon: 'error',
title: 'Oops...',
text: error.response.data
});
console.log('error', error.response.data);
}
}
};
}
As i can see in your Error Screenshot you have defined email and policy field required in your mongoose Schema. Something like this...
email: {
type: String,
required: true
}
Your server is not getting these two required fields, make sure you are sending those in axios Post Request Properly !

xhr.js:210 DELETE http://localhost:3000/posts/62575cb61cb27c6417732193 403 (Forbidden) / cannot delete document

I am using axios for the request of my own api, everything works correctly except the DELETE request, I looked for information about the error but I have not found the solution. when I send the request to the server I get this error: "xhr.js:210 DELETE http://localhost:3000/posts/62575cb61cb27c6417732193 403 (Forbidden)".
I put this line of code in Package.Json to avoid problems with CORS:
"proxy": "http://localhost:8080/api"
This would be my api, for the operation I pass the post id by url and the user id:
(I tried it in postman and it works without any problem)
router.delete("/:id", async (req, res) => {
try {
const post = await Post.findById(req.params.id);
if (post.userId === req.body.userId) {
await post.deleteOne();
res.status(200).json("the post has been deleted");
} else {
res.status(403).json("you can delete only your post");
}
} catch (err) {
res.status(500).json(err);
}
});
and this is where I consume my api:
const Post = ({ date, _id,userId, description }) => {
const handleDelete = async () => {
try {
await axios.delete('posts/' + _id, { userId: currentUser._id })
} catch (error) {
console.log(error)
}
}
return(
<div onClick={handleDelete }>
//component content
</div>
)
}
export default Post
I solved it, sending a data object to my api (inside that object I put the user id):
const handleDelete = async () => {
try {
await axios.delete('/posts/' + _id, {
data: {
userId: currentUser._id
}
}
)
} catch (error) {
console.log(error)
}
}

MEAN stack how to find _id from database to send a PUT request

I'm having a problem identifying a 'task' in mongoDB from my frontend angular.
This question is the most similar to my question but here it just says req.body.id and doesn't really explain how they got that.
This question involves what I am trying to do: update one document in a collection upon a click. What it does in the frontend isn't important. I just want to change the status text of the Task from "Active" to "Completed" onclick.
First I create a task and stick it in my database collection with this code:
createTask(): void {
const status = "Active";
const taskTree: Task = {
_id: this._id,
author: this.username,
createdBy: this.department,
intendedFor: this.taskFormGroup.value.taskDepartment,
taskName: this.taskFormGroup.value.taskName,
taskDescription: this.taskFormGroup.value.taskDescription,
expectedDuration: this.taskFormGroup.value.expectedDuration,
status: status
};
this.http.post("/api/tasks", taskTree).subscribe(res => {
this.taskData = res;
});
}
When I make this post to the backend, _id is magically filled in!
I'm just not sure how I can pass the id to the put request in nodejs router.put('/:id') when I'm pushing it from the frontend like this:
completeTask(): void {
const status = "Completed";
const taskTree: Task = {
_id: this._id,
author: this.username,
createdBy: this.department,
intendedFor: this.taskFormGroup.value.taskDepartment,
taskName: this.taskFormGroup.value.taskName,
taskDescription: this.taskFormGroup.value.taskDescription,
expectedDuration: this.taskFormGroup.value.expectedDuration,
status: status
};
console.log(taskTree);
this.http.put("/api/tasks/" + taskTree._id, taskTree).subscribe(res => {
this.taskData = res;
console.log(res);
});
}
In the template I have a form that's filled in and the data is immediately outputted to a task 'card' on the same page.
When I send the put request from angular, I get a response in the backend just fine of the response I ask for in task-routes.js:
router.put("/:id", (req, res, next) => {
const taskData = req.body;
console.log(taskData);
const task = new Task({
taskId: taskData._id,
author: taskData.author,
createdBy: taskData.createdBy,
intendedFor: taskData.intendedFor,
taskName: taskData.taskName,
taskDescription: taskData.taskDescription,
expectedDuration: taskData.expectedDuration,
status: taskData.status
})
Task.updateOne(req.params.id, {
$set: task.status
},
{
new: true
},
function(err, updatedTask) {
if (err) throw err;
console.log(updatedTask);
}
)
});
The general response I get for the updated info is:
{
author: 'there's a name here',
createdBy: 'management',
intendedFor: null,
taskName: null,
taskDescription: null,
expectedDuration: null,
status: 'Completed'
}
Now I know _id is created automatically in the database so here when I click create task & it outputs to the 'card', in the console log of task after I save() it on the post request, taskId: undefined comes up. This is all fine and dandy but I have to send a unique identifier from the frontend Task interface so when I send the 'put' request, nodejs gets the same id as was 'post'ed.
I'm quite confused at this point.
So I finally figured this out...In case it helps someone here's what finally worked:
First I moved my update function and (patch instead of put) request to my trigger service:
Trigger Service
tasks: Task[] = [];
updateTask(taskId, data): Observable<Task> {
return this.http.patch<Task>(this.host + "tasks/" + taskId, data);
}
I also created a get request in the trigger service file to find all the documents in a collection:
getTasks() {
return this.http.get<Task[]>(this.host + "tasks");
}
Angular component
Get tasks in ngOnInit to list them when the component loads:
ngOnInit() {
this.triggerService.getTasks().subscribe(
tasks => {
this.tasks = tasks as Task[];
console.log(this.tasks);
},
error => console.error(error)
);
}
Update:
completeTask(taskId, data): any {
this.triggerService.updateTask(taskId, data).subscribe(res => {
console.log(res);
});
}
Angular template (html)
<button mat-button
class="btn btn-lemon"
(click)="completeTask(task._id)"
>Complete Task</button>
// task._id comes from `*ngFor="task of tasks"`, "tasks" being the name of the array
//(or interface array) in your component file. "task" is any name you give it,
//but I think the singular form of your array is the normal practice.
Backend Routes
GET all tasks:
router.get("", (req, res, next) => {
Task.find({})
.then(tasks => {
if (tasks) {
res.status(200).json(tasks);
} else {
res.status(400).json({ message: "all tasks not found" });
}
})
.catch(error => {
response.status(500).json({
message: "Fetching tasks failed",
error: error
});
});
});
Update 1 field in specified document (status from "Active" to "Completed"):
router.patch("/:id", (req, res, next) => {
const status = "Completed";
console.log(req.params.id + " IT'S THE ID ");
Task.updateOne(
{ _id: req.params.id },
{ $set: { status: status } },
{ upsert: true }
)
.then(result => {
if (result.n > 0) {
res.status(200).json({
message: "Update successful!"
});
}
})
.catch(error => {
res.status(500).json({
message: "Failed updating the status.",
error: error
});
});
});
Hope it helps someone!

GET request works on backend with Postman, but returns null to frontend

I have a GET request where if I add the creator to the post as a param like api/watchlist/?creator=5dac9d3567aca81e40bfc0 and test it in Postman, the results return all posts by that creator with the following code correctly.
app.js
app.get('/api/watchlist',(req, res, next)=>{
Watching.find({ creator: req.query.creator})
.then(documents => {
console.log("Creator value: " + req.query.creator);
res.status(200).json({
message: 'User\'s watchlist items retrieved!',
posts: documents
});
});
});
If I try and pull the GET request into angular, then it returns nothing.
Also the front end when I open the relevant webpage says frontend is saying ERROR TypeError: "retrievedData.posts is undefined"
getWatchListItems watch-list.service.ts:72
watch-list.service.ts
getWatchListItems(creator: string) {
this.http
.get<{ message: string; posts: any; maxPosts: number; watchlist: string }>(
"http://localhost:3000/api/watchlist?creator=" + creator
)
.pipe(
map(retrievedData => {
console.log(retrievedData.watchlist);
return {
posts: retrievedData.posts.map(post => {
console.log(retrievedData.watchlist);
return {
title: post.title,
cost: post.cost,
listingOwner: post.listingOwner,
id: post._id
};
}),
maxPosts: retrievedData .maxPosts
};
})
)
.subscribe(transformedPostData => {
console.log(transformedPostData);
this.posts = transformedPostData.posts;
this.postsUpdated.next({
listing: [...this.posts],
postCount: transformedPostData.maxPosts
});
});
}
watching.component.ts
ngOnInit() {
this.userId = localStorage.getItem("userId: ");
// this.userId = this.authService.getUserId();
this.watchListService.getWatchListItems(this.userId);
this.postsSub = this.watchListService
.getPostUpdateListener()
.subscribe((postData: { listing: Watching[]; postCount: number }) => {
this.totalPosts = postData.postCount;
this.posts = postData.listing;
});
}
You are sending the create as a route parameter in angular service.
So you need to modify your api like this:
app.get('/api/watchlist/:creater', (req, res, next) => {
Watching.find({ creator: req.params.creator })
.then(documents => {
console.log("Creator value: ", req.params.creator);
res.status(200).json({
message: 'User\'s watchlist items retrieved!',
watchlist: documents
});
});
});
And if you dont want to change the api, you should send the creator as a query param like this:
return this.http
.get<{ message: string; posts: any; maxPosts: number }>(
"http://localhost:3000/api/watchlist?creator=" + creator
)
After this, you must handle the response data in angular, and transform to your needs.
Can you update the question, by including this console.log result with this code? (as I know in the service you don't need to subscribe, you are subscribing in your component)
getWatchListItems(creator: string) {
return this.http("http://localhost:3000/api/watchlist?creator=" + creator)
.pipe(
map(retrievedData => {
console.log("retrievedData: ", retrievedData);
//todo: transform retrievedData
return retrievedData;
})
)
}
Don't forget your api returns the data in this format, so you should
{
"watchlist": ...
"message": ...
}

Resources