Having trouble Updating an item with react-admin <Edit/> component - node.js

Trying to get a simple edit view to update products. Right now I can successfully click the edit button on a product item in a list and the edit view renders with the product data passed into the form. However, when I edit a field and hit the save button the getOne route handler on my backend is hit while the save button stays permanently pressed with a loading symbol on it.
Meanwhile the onSuccess function for my <Edit /> throws an error in the JavaScript console saying its data prop was undefined leading me to believe nothing was returned to the component. By setting breakpoints and console.logs I'm pretty confident that the update method is being hit by the save button but I can't figure out why the update method would be hitting the getOne route handler on the backend and why it wouldn't be receiving anything back from it. The only thing I've really tried aside from basic tinkering and console logging was to give unique routes to update because the url is the same as getOne it just has a different method but it just caused more errors.
You can clone the repo or scroll down for code snippets
Git clone link: https://github.com/cbddy/wholesale-portal.git
Steps to reproduce:
start up the app by running npm run buil, npm run server then go to http://localhost:5000/admin#/admin-products. You must be on react-admin branch (git checkout react-admin). Also, you must rebuild and restart the server after every change.
click the show button on one of the items in the list
click edit in top right corner of the show comp
edit one of the fields and hit save
You will see the getOne route handler firing in the server and the onSuccess func throw it's error in the js console in Chrome
relevant files:
frontend
src/components//adminSetup/dataProvider.js
src/components//adminSetup/Setup.js
src/components//adminSetup/MenuItems/Products.js
backend
-server/server.js
-server/admin-routes/admin-product.router.js
React-admin version: 3.7.0
Last version that did not exhibit the issue (if applicable):
React version: 16.13.1
Browser: Google Chrome
Stack trace (in case of a JS error): ?
Here is the relevant code and errors.
edit component
export const ProductEdit = props => {
const notify = useNotify();
const refresh = useRefresh();
const redirect = useRedirect();
const onSuccess = ({ data }) => {
notify(`Changes to product "${data.name}" saved`)
redirect('/admin-products');
refresh();
};
return (
<Edit onSuccess={onSuccess} actions={<ProductEditActions />} title={<ProductTitle />} {...props}>
<SimpleForm>
{/* {!} NEEDS TO BE DONE LATER TO EDIT ORDERS OF USERS */}
{/* <ReferenceInput source="orderId" reference="orders"> */}
<TextInput label="Product Name" source="name" />
<TextInput label="Category" source="category"/>
<TextInput label="Description" source="description" options={{ multiLine: true }} />
<NumberInput label="Price" source="price"/>
{/* <ImageInput source="imageData"/> */}
</SimpleForm>
</Edit>
)
}
DataProvider methods for getOne and update
getOne: (resource, params) => {
console.log("getOne resource", resource);
return httpClient(`${apiUrl}/${resource}/${params.id}`)
.then(({ json }) => {
console.log(json);
return { data: json };
})
},
update: (resource, params) => {
console.log("update", resource);
return httpClient(`${apiUrl}/${resource}/${params.id}`, {
method: "PUT",
body: JSON.stringify(params.data),
}).then(({ json }) => {
return { data: json }
}).catch(err => {
return displayErrorMessage(err)
})
},
backend route handlers for getOne and update
//getOne
router.get("/:id", async (req, res) => {
console.log("req method :", req.method)
console.log("req.data :", req.data)
console.log("getOne hit. Id: ", req.params.id)
await Product.findOne({_id: req.params.id})
.then(async (product) => {
product = await JSON.parse(JSON.stringify(product).split('"_id":').join('"id":'));
console.log("parsed product: ", product)
res.json(product)
}).catch(err => {
console.log("error: ", err)
res.status(500).send("user not found.")
})
})
router.put("/:id", async (req, res) => {
console.log("update hit")
console.log("id: ", req.params.id)
console.log("body: ", req.body)
await Product.updateOne({_id: req.params.id}, req.body)
.then(async (product) => {
product.save();
product = await JSON.parse(JSON.stringify(product).split('"_id":').join('"id":'));
return res.status(200).json(product)
}).catch(err => {
console.log(err)
res.status(500).send("Failed to update.")
})
})
My browser directly after hitting save

Related

What is the process for sending a axios POST method using reactjs to a node path?

I am trying to send a POST request using axios to the backend but it is throwing a 404 for the path and i dont know why
Here is the react/redux code calling the axios request
export const addGoal = (newGoal: Goal) => {
return (dispatch: any) => {
authMiddleWare(history)
const newValues = newGoal
const authToken = localStorage.getItem('AuthToken')
axios.defaults.headers.common = { Authorization: `${authToken}` }
axios
.post('/goal', newValues)
.then((response) => {
console.log('success', response.data)
dispatch({
type: ADD_GOAL,
payload: response.data,
})
})
.catch((err) => {
console.error('\nCould not submit goal\n', err.response)
})
}
}
This is the nodejs path i have in my main backend file for calling the paths
app.post("/goal", auth, postOneGoal);
This is the backend function for the node path
// ADDS A SINGLE WORKOUT
exports.postOneGoal = (request, response) => {
if (request.body.id.trim() === "" || request.body.text.trim() === "") {
return response.status(400).json({ body: "Must not be empty" });
}
const newGoalItem = {
username: request.user.username,
id: request.body.id,
text: request.body.text
};
db.collection("goals")
.add(newGoalItem)
.then((doc) => {
const responseNewGoalItem = newGoalItem;
responseNewGoalItem.id = doc.id;
doc.update(responseNewGoalItem);
return response.json(responseNewGoalItem);
})
.catch((err) => {
response.status(500).json({ error: "Couldn't add the goal" });
console.error(err);
});
};
I am using a firebase url proxy in my package.json as well.
Let me know if any more info is needed
Posting this as Community Wiki, based in the comments.
Considering the fact that you are using Cloud Functions, you will need to redeploy the functions everytime you update your code. You can check more details on deploying your functions in the official documentation accessible here. There you will have the options regarding how and where you can deploy your functions for better testing.

Node JS GET API's not working with new data

I am using Node JS and MYSQL. When I add new data to my project, it writes to the database. Then when I want to GET this data with another API, it doesn't come. When I try again after about a minute, it comes on. However, right after I send a request via Swagger, data comes from the outside (Postman or Panel) on my request again.
My simple Controller.
exports.GetAll = (req, res, next) => {
ThisModel.GetAllSQL()
.then((response) => {
res.status(200).json(response[0]);
}).catch((error) => {
res.status(400).send();
console.log('Senaryo listesi çekilirken bir hata meydana geldi: ' + error);
})
}
.then((response) => {
res.status(200).json(response[0]);
})
Judging from the line above, it looks like you're getting a list/array of data, but only returning the first item in the list response[0].
Maybe this is what you're looking for:
.then((response) => {
res.status(200).json(response);
})

Axios 'GET' request is pending but it never reaches the server

I am using axios on my React app to get data from my server (Node). My GET request stays pending in chrome developer tools and does not reach the server if I refresh the app on the provided route (e.g., http://localhost:3000/category/5d68936732d1180004e516cb). However, if I redirect from the home page, it will work.
I have tried different variations of making sure I end my responses on the server end of things.
Several posts have had related problems (e.g., request not reaching the server, POST request does not reach the server) but unfortunately not been helpful in my situation.
Here is my main call in my react app:
componentDidMount() {
console.log('I am here!'); // this gets executed even on page refresh
axios.get(`/api/categories/${this.props.id}`)
.then( (res) => {
this.setState({
title: res.data.category.title,
module: res.data.category.module ? true : false,
...res.data
})
}, (err) => console.log(err))
.catch(err => console.log(err));
}
On my back end I call this function after going through user verification:
module.exports.publishedCategories = async function(req, res) {
try {
// some code that I removed for clarity
res.json({
category,
children,
flagged: flaggedCategories
});
} catch(err) {
console.log(err);
res.sendStatus(500).end();
}
}
Some more code regarding my routing:
index.js
<Route
path='/category/:id'
render={ (props) => {
return <Category id={props.match.params.id} />
}}
/>
I do not get any error messages...
I was overzealous with my initial solution of switching to componentDidUpdate(). This only worked for page refreshes but not for redirects (i.e., I had the reverse problem). My final solution is as follows:
componentDidMount = async () => {
setTimeout( async () => {
this.loadCategory();
}, 10)
}
componentDidUpdate = async (props, state) => {
if(props.match.params.id !== this.props.match.params.id) {
this.loadCategory();
return;
}
return false;
}
loadCategory = async () => {
let result = await axios.get(`/api/categories/${this.props.match.params.id}`);
this.setState({
title: result.data.category.title,
module: result.data.category.module ? true : false,
...result.data
});
}
I am not sure why adding a setTimeout() works for componentDidMount().
As per the documentation componentDidUpdate(), it is good for netork updates however you need to compare prior props with current props.
Unforunately I am not sure how to get around the setTimeout() hack but it seems to work.

Client side can't fetch server response

The Problem
I deployed a create-react-app webapp to aws ec2. It's used to display data from a database and send data to it. I use ExpressJS, CORS and MySQL.
With the following code i fetch the corresponding URL and the server.js sends back the database content. Until here, everything works fine.
getBets = _ => {
fetch("http://ec2***.amazonaws.com
.then(response => response.json())
.then(response => this.setState({bets: response.data}))
.catch(err => console.error(err))
};
The problem begins when sending data to the database with the following code:
addBet = _ => {
const { bet } = this.state;
fetch(`http://ec2***.amazonaws.com/bets/add?name=${bet.person_name}&bet=${bet.time_bet}`)
.then(response => response.json())
.then(this.getBets)
.catch(err => console.error(err))
};
On click the addBet-function populates the db, but in chrome I following error:
GET http://ec2***.amazonaws.com/bets/add?name=Peter%20Pan5&bet=10:17%205 net::ERR_EMPTY_RESPONSE
and
TypeError: Failed to fetch
Regarding chrome dev-tools, the first error corresponds to the fetch in the addBet function and the second error to the catch part.
On the server side I've the following code for processing the fetch:
app.get("/bets/add", (req, res) => {
const {name, bet} = req.query;
const INSERT_BET = `INSERT INTO bets (name, bet, timestamp) VALUES("${name}", "${bet}", CURTIME())`;
connection.query(INSERT_BET, (err, res) => {
if (err) {
return res.send(err);
}
else {
return res.send("succesfully added your bet");
}
})
});
I want to mention, that the res paramter in the app.get part is unused. That tells me my IDE.
After a lot of hours digging deeper in the topics of expressJS and the fetch api, I guess, that the app.get part doesn't send a response to the server. But the fetch need some response.
My Question
How do I have to change the code in the app.get part to send a proper response back to the server?
AND
Am I right with my guess?
In MYSQL when you do an insert query you get back err,results and fields in the callback function like this:
connection.query('INSERT INTO posts SET ?', {title: 'test'}, function (error,
results, fields) {
if (error) throw error;
console.log(results.insertId);
});
You have used the parameter res for result and then you have used res.send() which now corresponds to that res parameter in the callback function and not the res object.Rewrite it like this:
app.get("/bets/add", (req, res) => {
const {name, bet} = req.query;
const INSERT_BET = `INSERT INTO bets (name, bet, timestamp) VALUES(?,?,?)`;
connection.query(INSERT_BET,[name,bet,CURTIME()] ,(err, result) => {
if (err) {
return res.send(err);
}
else {
return res.send("succesfully added your bet");
}
})
});
I have also used prepared statement in place of normal sql queries. These are used to prevent sql injections. I hope it will work now.

How to properly render server-side errors in the client for Node/Express?

I've setup a simple Node/Express service that creates new bookmarks to a database.
In my Bookmark schema, one of the fields, url, has validations. When you try to create a new bookmark without the correct validations, it throws an error and a message.
E.g. if you don't provide a url to the request, then it will throw a message Please provide a url.
The route for the POST request to create a new bookmark:
app.post(
'/api/bookmarks',
(req, res) => {
const { url, tags } = req.body;
const bookmark = new Bookmark({
url,
tags
});
bookmark.save()
.then(res => {
res.send(result)
})
.catch(error => {
console.log("err:", error);
res.status(400).send(error)
});
}
);
What my Bookmark schema looks like:
const bookmarkSchema = new Schema({
url: {
type: String,
required: [true, "Please provide a URL."],
match: [urlRegex, "Your URL is invalid. Please make sure to add www."]
},
tags: [String]
});
When I make an API call on the front end client (e.g. via React), I am able to catch the error and subsequent message (Please provide a URL.), but as you can see, I have to call err.response.data.errors.url.message which seems very verbose and long winded.
axios.post('http://localhost:5000/api/bookmarks', { tags: ["banana", "orange"] })
.then(result => console.log('Result:', result.data))
.catch(err => console.log('Error:', err.response.data.errors.url.message));
What has me thinking - is there a better way to render errors here? Would it be better handle errors in the client side instead?
I'm quite new to error handling so would like to know what is considered best practice.
Thanks!
Something like this. So you don't have to check it in schema.
app.post(
'/api/bookmarks',
(req, res) => {
const { url, tags } = req.body;
if (!url) {
res.status(400).send({error: "URL is missing"})
return;
}
if (!url.match(urlRegex) {
res.status(400).send({error: "URL is not valid"})
return;
}
const bookmark = new Bookmark({
url,
tags
});
bookmark.save()
.then(res => {
res.send(result)
})
.catch(error => {
console.log("err:", error);
res.status(500)
});
}
);
It's a bad practice to send server side errors to frontend. It's a easy way to leak information about your database/backend. You should send your own string message instead of catched error.

Resources