NodeJS PUT request returns error 400 (Bad Request) - node.js

I am trying to do a PUT request with NodeJS, Express and MongoDB. The issue that I am currently having is that I keep receiving an error **400** and I am not sure exactly why.
What I am exactly trying to do is upload edit a field in my USER collection, after a certain user has been registered. This is supposed to happen on a specific /user/edit/:id route.
My application is structured with a standard MVC pattern.
Here is how my Mongo Schema is structured:
let UserSchema = new mongoose.Schema({
username: String,
password: String,
email: String,
avatar: String,
firstName: String,
lastName: String,
laps:[{ type: Schema.Types.ObjectId, ref: 'Stats' }]
});
This is my service:
exports.updateUser = async function(user) {
let id = user.id;
let oldUser;
try {
//Find the old User Object by the Id
oldUser = await User.findById(id);
} catch(e) {
throw Error("Error occured while Finding the User");
}
// If no old User Object exists return false
if (!oldUser) {
return false;
}
//Edit the User Object
oldUser.firstName = user.firstName || oldUser.firstName;
oldUser.lastName = user.lastName || oldUser.lastName;
oldUser.avatar = user.avatar || oldUser.avatar;
try {
let savedUser = await oldUser.save();
return savedUser;
} catch(e) {
throw Error("And Error occured while updating the User");
}
};
The Controller that I am using:
exports.updateUser = async function(req, res, next) {
if (!req.body._id){
return res.status(400).json({status: 400, message: "Id must be present"})
}
let id = req.body._id;
let user = {
id,
firstName: req.body.firstName || null,
lastName: req.body.lastName || null,
avatar: req.body.avatar || null
};
try {
let updatedUser = await UserService.updateUser(user);
return res.status(200).json({status: 200, data: updatedUser, message: "Successfully Updated User"})
} catch(e) {
return res.status(400).json({status: 400, message: e.message})
}
};
Route path in router file:
router.post('/edit/:id', UserController.updateUser);
Route path for users inside server file:
app.use('/user', require('./api/routes/user.route'));
I know that most 4** errors come from the front end of the application, so I will also post my form and the constructor behind it. I am using ReactJS as a framework.
Front end Form:
class UserProfile extends Component {
constructor(props) {
super(props);
this.state = {
avatar: '',
resultsSubmitted: false
};
this.formChange = this.formChange.bind(this);
this.resultsSubmit = this.resultsSubmit.bind(this);
}
formChange(e) {
console.log("form changed" + e.target);
const { name, value } = e.target;
this.setState({ [name]: value });
}
resultsSubmit(e) {
e.preventDefault();
const accessToken = JSON.parse(localStorage.getItem('auth_user')).data.access_token;
const { avatar } = this.state;
const { dispatch } = this.props;
if (avatar) {
console.log("submitting results: " + avatar);
dispatch(userActions.addAvatar(avatar, accessToken));
}
}
render(){
const { avatar, resultsSubmitted} = this.state;
return (
<div className="container-fluid no-gutters page-login">
<div className="row">
<div className="login-wrapper">
<h2> Edit User Profile </h2>
<form onSubmit={this.resultsSubmit}>
<div className="form-group">
Paste Avatar URL: <input type="text" value={avatar} name="avatar" id="" onChange={this.formChange} />
</div>
<input type="submit" className="btn btn-primary btn-lg btn-block" value="submit"/>
</form>
</div>
</div>
</div>
)
}
}
function mapStateToProps(state) {
const { layout } = state;
return {
layout
};
}
export default connect(mapStateToProps)(UserProfile);
My dispatch:
function addAvatar(avatar, token) {
return dispatch => {
dispatch(request());
userService.addAvatar(avatar, token)
.then(
user => {
dispatch(success(user));
history.push(`${process.env.PUBLIC_URL}/`);
},
error => {
dispatch(failure(error));
dispatch(alertActions.error(error));
}
);
};
function request() { return { type: userConstants.AVATAR_REQUEST } }
function success(user) { return { type: userConstants.AVATAR_SUCCESS, user } }
function failure(error) { return { type: userConstants.AVATAR_FAILURE, error } }
}
HTTP Post service:
function addAvatar(avatar){
const requestOptions = {
method: 'POST',
headers: authHeader(),
body: avatar
};
return fetch('http://localhost:3003/user/edit/:id', requestOptions)
.then(response => {
if (!response.ok) {
console.log("+",response,"+");
return Promise.reject(response.statusText);
}else{
console.log(response, "the user service response was gooooooooooood");
}
return response.json();
})
.then(data => console.log(data,"WHT DO WE HAVE HERE?"));
}
Apologies for the huge code wall but I wanted to include all the bits.
I am getting an error 400 (Bad Request) on the route POST
http://localhost:3003/user/edit/:id

In your fetch request you are sending only avatar as a body and on your updateUser function you have the following if statement:
if (!req.body._id){
return res.status(400).json({status: 400, message: "Id must be present"})
}
so obviously you have not _id on your body request but an avatar instead, in fact you're sending your id as a param
'http://localhost:3003/user/edit/:id'
So you could change this line as a workaround
if (!req.params.id){
Hope it helps.

The below snippet shows that you are trying to get the ID parameter from the body of the request.
if (!req.body._id){
return res.status(400).json({status: 400, message: "Id must be present"})
}
Whereas, the route /user/edit/:id , shows that the ID parameter is actually passed through the URL, and to access it, all you need is to get your ID from the URL using req.params.id. req.params contains all parameters that are passed through the route or URL path.
The above snippet should be corrected to;
if (!req.params.id){
return res.status(400).json({status: 400, message: "Id must be present"})
}
Check https://expressjs.com/en/guide/routing.html#route-parameters for proper guide on how to deal with route parameter.

Related

React use axios return Axios Error {message: 'Network Error'} and TypeError: Cannot read properties of undefined (reading 'path')

when I click submit button, then web and terminal will return error like me title
but i try postman is ok , so i think is my axios setting error,how can i fixed this error? I found many similar questions, but can't not help me
the other question is , my form tag action is "/addItems", but i sending request , i got this error CANNOT POST / addItems Post http://localhost:3000/addItems 404 (Not Found)
(axios setting )
post(id, title, description, price, avatar) {
let token;
if (localStorage.getItem("user")) {
token = JSON.parse(localStorage.getItem("user")).token;
} else {
token = "";
}
const formData = new FormData();
// formData.append("id", id);
// formData.append("title", title);
// formData.append("description", description);
// formData.append("price", price);
formData.append("avatar", avatar);
return axios.post(
API_URL + "/addItems",
{ formData },
{
headers: {
Authorization: token,
"Content-Type": "multipart/form-data"
}
}
);
}
(item.route)
itemRouter.post("/addItems", upload.single("avatar"), async (req, res) => {
let { id, title, description, price, avatar } = req.body;
if (req.user.isMember()) {
return res.status(400).send("Only admin can add new items");
}
console.log(req.file);
avatar = req.file.path;
const newItem = new Item({
id,
title,
description,
price,
avatar
});
try {
await newItem.save();
console.log(req.file);
res.status(200).send("New item has been saved.");
} catch (err) {
res.status(400).send("Error");
console.log(err);
}
});
(addItemsComponent)
const handleChangePost = () => {
if (currentUser.user.role !== "admin") {
window.alert("Member can't not post item!! ");
navigate("/");
} else {
ItemService.post(avatar)
.then(() => {
window.alert("Post successfully");
navigate("/");
})
.catch((error) => {
console.log(error);
console.log(error.response);
setErrorMessage(error.response.data);
});
}
};
return (
<div>
<form action="/addItems" method="post" enctype="multipart/form-data">
<input onChange={handleChangeAvatar} value={avatar} type="file" name="avatar" />
<button type="submit" onClick={handleChangePost}>
Submit
</button>
</form>
</div>
);
Try this
return axios.post(API_URL + "/addItems", formData,
{
headers: {
Authorization: token,
"Content-Type": "multipart/form-data"
}
}
);

How to create a comment and reply section with MERN STACK

I created a comment session on my entertainment website
It’s working on backend.
It’s working on the frontend also but it’s not displaying the content the user typed on the database
This is my frontend (Comment form) logic:
export default function AddComment({ busy}) {
const [content, setContent] = useState("");
const { movieId } = useParams();
const { updateNotification } = useNotification();
const handleOnChange = ({ target }) => {
setContent(target.value);
};
const handleSubmit = async (e) => {
e.preventDefault();
const { error, message } = await addComment(movieId);
if (error) return updateNotification("error", error);
updateNotification("success", message);
const newComment = {
content,
};
setContent(newComment);
setContent("");
};
return (
<div className='p-5'>
<br />
<p className='dark:text-white text-primary'>replies</p>
<hr className='w-64' />
{/* Comment Lists */}
{/* Root Comment Form */}
{/* Form */}
<form className='flex ' onSubmit={handleSubmit} busy={busy}>
<textarea
value={content}
onChange={handleOnChange}
type='text'
autoComplete='text'
className='w-full rounded-md p-2 dark:text-white text-primary outline-none bg-transparent resize-none border-b focus:border-blue-500'
placeholder='Add New comment'
/>
<br className='dark:text-white text-primary ' />
<button
type='submit'
className=' w-5 h-14 dark:text-white text-primary bg-blue-600 hover:bg-blue-400 focus:border-blue-900 rounded-md'
>
{busy ? <ImSpinner3 className='animate-spin' /> : "Add"}
</button>
</form>
</div>
);
}
Then the addComment is coming from this API:
import { catchError, getToken } from "../utils/helper";
import client from "./client";
export const addComment = async (movieId, newComment) => {
const token = getToken();
try {
const { data } = await client.post(
`/comments/comment/${movieId}`,
newComment,
{
headers: {
authorization: "Bearer " + token,
},
}
);
return data;
} catch (error) {
return catchError(error);
}
};
The backend is working:
exports.createComment = expressAsyncHandler(async (req, res) => {
const { movieId } = req.params;
const { content } = req.body;
const userId = req.user._id;
console.log(req.body);
// verify user before comment
if (!req.user.isVerified)
return sendError(res, "Please verify your email first!");
if (!isValidObjectId(movieId)) return sendError(res, "Invalid Movie!");
// create and update new comment
const newComment = new Comment({
user: userId,
parentMovie: movieId,
content,
});
// save new comment
await newComment.save();
res.json({ message: "New comment added!!", newComment });
});
I posted with Postman on backend it gave me this on the database:
_id
:
62dcfccd93444cef55611632
user
:
62bf20d65073a7c65f549078
parentMovie
:
62c2c425465804ff32cdd06c
content
:
"hello"
createdAt
:
2022-07-24T08:03:25.666+00:00
updatedAt
:
2022-07-24T08:03:25.666+00:00
__v
:
0
on the console:
The port is listening on port 8000
connected to db
{ content: 'hello' }
POST /api/comments/comment/62c2c425465804ff32cdd06c 200 447.534 ms - 260
I posted on the frontend it gave me this on the database, no content:
_id
:
62dcfd6993444cef55611635
user
:
62bf57e8a8f3e737b2af23d9
parentMovie
:
62cc1d426785cfe42f8737a8
createdAt
:
2022-07-24T08:06:01.458+00:00
updatedAt
:
2022-07-24T08:06:01.458+00:00
__v
:
0
on the console it shows an empty object:
{}
POST /api/comments/comment/62cc1d426785cfe42f8737a8 200 364.009 ms - 242
This is how I solved the problem
Hope this solution will help many
const handleSubmit = async (e) => {
e.preventDefault();
const { error, message } = await addComment(movieId, content); // call the content and movieId from backend
if (error) return updateNotification("error", error);
updateNotification("success", message);
// push and display the content on database
const newComment = {
content,
};
setContent(newComment);
setContent("");
};
Then the API should be like this
export const addComment = async (movieId, newComment) => {
const token = getToken();
// console.log(newComment);
const body = {
content: newComment,
};
try {
const { data } = await client.post(`/comments/comment/${movieId}`, body, {
headers: {
authorization: "Bearer " + token,
},
});
return data;
} catch (error) {
return catchError(error);
}
};

how to update context state in react

I am having a problem that when user upload their profile image it did not change, user have to log out and log back in to make a change complete.
Here is my back end how to get image from client and store it on cloudinary:
profilesController.js:
exports.updateAvatar = async (req, res) => {
// Find user with matching token
// const updates = [];
const updateUserAvatar = await models.User.findOne({
where: {
id: req.id,
},
});
// Was user found?
if (updateUserAvatar === null) {
return res.status(200).json({
validationErrors: {
errors: [
{
msg: "Reset is invalid or has expired.",
},
],
},
});
}
// Update user with new info
models.User.update(
{
picture: req.imageUrl,
},
{
where: {
id: updateUserAvatar.dataValues.id,
},
}
);
console.log(updateUserAvatar);
At the console it should gave me a new image url but instead it just keep the old image url
Here is my profilesAPI where my route is:
router.post('/upload/image', function (req, res, next) {
const dUri = new Datauri();
const dataUri = (req) => dUri.format(path.extname(req.name).toString(), req.data);
if (req.files !== undefined && req.files !== null) {
const { file, id } = req.files;
const newFile = dataUri(file).content;
cloudinary.uploader.upload(newFile)
.then(result => {
const imageUrl = result.url;
const data = {id : req.body.id, imageUrl };
updateAvatar(data);
return res.status(200).json({ message: 'Success', data: { imageUrl } });
}).catch(err => res.status(400).json({message:'Error', data: { err}}));
} else {
return res.status(400).json({ message: 'Error' });
}
});
And that's all for my back end code. Here is my front end that cient send image to server:
Here is the method that help user can send image to server:
const UserCard = ({ name, userEmail, isVerified, id, updateUserAvatar, currentUser }) => {
const [selectedValue, setSelectedValue] = useState("a");
const handleChange = (event) => {
setSelectedValue(event.target.value);
};
const [imageSelected, setImageSelected] = useState("");
const uploadImage = () => {
const formData = new FormData();
formData.append("file", imageSelected);
formData.append("id", id);
axios
.post("/api/v1/profiles/upload/image", formData, {
headers: { "Content-Type": "multipart/form-data" },
})
.then((response) => {
updateUserAvatar(response.data.data.imageUrl);
});
};
useEffect(() => {
if (imageSelected !== '') {
uploadImage();
}
}, [imageSelected]);
return (
<div className="avatar--icon_profile">
<Card className="profile--card_container">
<CardContent>
{currentUser.picture ? (
<div>
<input
className="my_file"
type="file"
ref={inputFile}
onChange={(e) => setImageSelected(e.target.files[0])}
/>
<div className="profile-image">
<Avatar
src={currentUser.picture}
alt="Avatar"
className="avatar--profile_image"
onClick={onButtonClick}
/>
</div>
</div>
and here is my Global State. I tried to update nested state in my context but seems like it didn't work.
const GlobalState = (props) => {
// User State -----------------------------------------------------------------------------
const [currentUser, setUser] = useState(props.serverUserData);
console.log(currentUser)
const updateUser = (userData) => {
setUser(userData);
};
// This method is passed through context to update currentUser Avatar
const updateUserAvatar = (picture) => {
setUser({ ...currentUser, picture: picture });
};
const providerValues = {
currentUser,
updateUser,
updateUserAvatar,
};
return (
<GlobalContext.Provider value={providerValues}>
{props.children}
</GlobalContext.Provider>
);
};
export default GlobalState;
and here is my console.log(currentUser) gave me:
{id: "a19cac5c-ea25-4c9c-b1d9-5d6e464869ed", name: "Nhan Nguyen", email: "nhan13574#gmail.com", publicId: "Nh1615314435848", picture: "http://res.cloudinary.com/teammateme/image/upload/v1617229506/gnlooupiekujkrreerxn.png", …}
email: "nhan13574#gmail.com"
id: "a19cac5c-ea25-4c9c-b1d9-5d6e464869ed"
isSessionValid: true
name: "Nhan Nguyen"
picture: "http://res.cloudinary.com/teammateme/image/upload/v1617229506/gnlooupiekujkrreerxn.png"
publicId: "Nh1615314435848"
__proto__: Object
Can anyone help me solve this problem? I really appreciate it
Added GlobalContext.js:
import React from "react";
const globalStateDefaults = {
modals: {
isAuthModalOpen: false,
modalToDisplay: "signup",
toggleModal: () => {},
setModalToDisplay: () => { },
},
user: undefined,
pageName: undefined,
loading: false,
teamProfileId: "",
userProfileId: "",
};
export const GlobalContext = React.createContext(globalStateDefaults);
You need to consume the context where you are trying to update user state.
const {currentUser, updateUser, updateUserAvatar} = React.useContext(GlobalContext)
Then you can call
updateUserAvatar(response.data.data.imageUrl)

mern - updated values are null in data

I'm trying to update the posts. The PUT request in the back end works fine, returning 200 and updates posts when tested on Postman however when I try to update a post in the front end (react), I'm not receiving any errors but the updated post isn't being updated on submit and the updated fields (title and body) are null. The updated values are null when I console.log(data) in the front end which is why they aren't being sent to the back end but they are shown correctly in post.
Why are the updated values null inside data? How can I update the post with the new values instead of getting null?
data:
post:
Updated code: Frontend
const EditPost = ({match}) => {
const [values, setValues] = useState({
title: "",
body: "",
error: ""
});
const [post, setPost] = useState({});
const { user, token } = isAuthenticated();
const {
title,
body,
error,
} = values;
const init = (id) => {
read(id).then(data => {
if (data.error) {
setValues({...values, error: data.error})
} else {
setValues({...values,
title: data.title,
body: data.body,
})
setPost({title: values.title, body: values.body})
}
})
}
useEffect(() => {
const id = match.params.id;
init(id);
}, []);
useEffect(() => {
setPost({...values });
}, [values.title, values.body]);
const handleChange = (name) => (event) => {
setValues({ ...values, [name]: event.target.value });
};
const clickSubmit = (event) => {
event.preventDefault();
setValues({ ...values, error: "" });
editPost(match.params.userId, match.params.id, token, post).then((data) => {
if (data.error) {
setValues({ ...values, error: data.error });
} else {
setValues({
...values,
title: "",
body: "",
error: false,
});
console.log(post)
console.log(data)
}
});
};
const newPostForm = () => (
<form onSubmit={clickSubmit}>
<div>
<input
onChange={handleChange("title")} type="text"
name="title"
value={title}
/>
</div>
<div className="form-group">
<textarea
onChange={handleChange("body")}
value={body} name="body"
/>
</div>
<button type="submit">Publish</button>
</form>
);
const showError = () => (
<div
style={{ display: error ? "" : "none" }}>
{error}
</div>
);
return (
<div>
{showError()}
{newPostForm()}
</div>
);
};
export default EditPost;
export const editPost = (userId, id, token, post) => {
return fetch(`${API}/${userId}/${id}/edit`, {
method: 'PUT',
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify(post)
})
.then(response => {
return response.json();
})
.catch(err => console.log(err));
};
postsByUser.js
<Link className="mypost_btn edit_btn" to={`/${_id}/${post._id}/edit`}>
Edit
</Link>
Backend code
exports.edit = (req, res) => {
if (!ObjectID.isValid(req.params.id))
return res.status(400).send(`ID is not valid: ${req.params.id}`)
const {title, body} = req.body
const updatedPost = {title, body }
Post.findByIdAndUpdate(req.params.id, {
$set: updatedPost
}, {new:true}, (error, data) => {
if (error) {
return error
} else {
res.send(data)
console.log(data)
}
})
}
Your problem lies here:
editPost(match.params.userId, match.params.id, token, post)
post is not defined.
Since post is not defined, no data is passed. Hence title and body equal to null. What you will need to do is, assuming from what I'm seeing on your code, is to define a state variable called post. I think you intended to do that:
const [post, setPost] = useState({values.title, values.body});
Then ensure that your post is updated whenever your values change using useEffect(),
useEffect(() => {
setPost({...values });
}, [values.title, value.body]);
So by the time you call your editPost() http-put-method, then post has a value. And it should work.
in EditPost.js editPost(match.params.userId, match.params.id, token).then((data) => { here you are missing the 4th arg which is the "post" it self you send to be updated

how to make API GET call for SpotifyAPI specifically, getUserPlaylists()?

I am trying to make a web application using the Spotify web API.
I used the Spotify auth-server code provided in their GitHub repo for my authorization code https://github.com/spotify/web-api-auth-examples
I followed the instructions here https://medium.com/#jonnykalambay/now-playing-using-spotifys-awesome-api-with-react-7db8173a7b13 to create a simple app that getsCurrentPlayback and displays the songs album art of what is now playing on users Spotify account.
I added a getFeatures function to get the valence of the song.
Right now I want to call the getUsersPlaylists() function from the spotify-web-api.
Once I can get the playlists to call to work I want to iterate through the tracks in the playlist to get each tracks valence. I think once I can get the playlist API call to work I can figure out the rest on my own.
(Help a poor college student out whose teammates don't do anything and who can't get help from the Prof because of COVID)
Thanks so much in advance!
Here is the code for my App.js. I am using REACT
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Spotify from 'spotify-web-api-js';
const spotifyWebApi = new Spotify();
class App extends Component {
constructor(){
super();
const params = this.getHashParams();
this.state ={
loggedIn: params.access_token ? true : false,
nowPlaying: {
name: "Not Checked",
image: '',
trackId: "",
valence: ""
},
idParams: {
userId: ""
},
playlistParam: {
playlistName: ""
},
myAccessToken: {
token: ""
}
}
if(params.access_token){
spotifyWebApi.setAccessToken(params.access_token);
}
}
getHashParams() {
var hashParams = {};
var e, r = /([^&;=]+)=?([^&;]*)/g,
q = window.location.hash.substring(1);
while ( e = r.exec(q)) {
hashParams[e[1]] = decodeURIComponent(e[2]);
}
return hashParams;
}
getToken() {
return spotifyWebApi.getAccessToken();
}
// Get user Id of logged in account user.
async getUserId(myToken)
{
const headers = {
Authorization: `Bearer ${myToken}`
};
let userId = '';
const response = await fetch('https://api.spotify.com/v1/me/playlists',
{
headers : headers
}
);
const jsonResponse = await response.json();
if(jsonResponse)
{
userId = jsonResponse.id;
}
return userId;
}
//***HERE IS SOME CODE I HAVE TRIED***
//
// fetch("https://api.spotify.com/v1/me/playlists?limit=1&offset=0", {
// headers: {
// Accept: "application/json",
// Authorization: "Bearer",
// "Content-Type": "application/json"
// }
// })
// fetch("https://api.spotify.com/v1/me/playlists", {
// headers: {
// Authorization: "Bearer {params.access_token}"
// //console.log("Have Gotten The Playlist");
// }
// });
//
// getUserPlaylist() {
// fetch("https://api.spotify.com/v1/me/playlists/", {
// headers: {
// Authorization: "Bearer {this.getToken()}"
// }
// });
//
// console.log("Have Completed It");
//
// }
//********
getNowPlaying() {
spotifyWebApi.getMyCurrentPlaybackState()
.then((response) => {
this.setState({
nowPlaying: {
name: response.item.name,
image: response.item.album.images[0].url,
trackId: response.item.id
}
});
})
}
getFeatures(){
spotifyWebApi.getAudioFeaturesForTrack(this.state.nowPlaying.trackId)
.then(
function(data){
console.log("Valence of song: ", data.valence);
},
function(err){
console.log(err)
}
);
}
render(){
return (
<div className="App">
<a href="http://localhost:8888">
<button>Login With Spotify</button>
</a>
<div> Now Playing: { this.state.nowPlaying.name } </div>
<div>
<img src={ this.state.nowPlaying.image } style={{width: 100}}/>
</div>
<div>
Track Id: { this.state.nowPlaying.trackId } </div>
<button onClick={() => this.getNowPlaying()}>
Check Now Playing
</button>
<button onClick={() => this.getFeatures()}>
Get Valence
</button>
<button onClick={() => this.getUserPlaylist()}>
Get your Playlists
</button>
<div> Your Selected Playlist : {this.state.playlistParam.playlistName}</div>
<button onClick={() => console.log(this.getUserId(this.params.access_token))}> Get User Id</button>
</div>
);
}
}
export default App;

Resources