I am using multer and cloudinary for backend to upload image to mongodb and getting req.file.path error and it seems I’m doing something wrong in the frontend when passing image because if I use POSTMAN, the api works and sign ups the user with image as a link.
Backend Code:
controller.js
const jwt = require("jsonwebtoken");
const bcrypt = require("bcryptjs");
const asyncHandler = require("express-async-handler");
const User = require("../models/userModel");
const registerUser = asyncHandler(async (req, res) => {
const { proImg, role, firstName, lastName, email, password } = req.body;
if (!firstName || !lastName || !email || !password) {
res.status(400);
throw new Error("Please add all the required fields");
}
//Check if the emails already exist
const userExists = await User.findOne({ email });
const sellerExists = await Seller.findOne({ email });
if (userExists || sellerExists) {
res.status(400);
throw new Error("Email already exists");
}
//Hash passwords
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);
//Create user
const user = await User.create({
proImg: req.file.path, //Getting error on this line
role,
firstName,
lastName,
email,
password: hashedPassword,
});
if (user) {
res.status(201).json({
_id: user.id,
proImg: user.proImg,
role: user.role,
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
token: generateToken(user._id),
});
} else {
res.status(400);
throw new Error("Invalid user data");
}
});
cloudinary.config.js
const multer = require("multer");
const cloudinary = require("cloudinary").v2;
const { CloudinaryStorage } = require("multer-storage-cloudinary");
const { CLOUDINARY_API, CLOUDINARY_SECRET, CLOUDINARY_HOST } = process.env;
cloudinary.config({
cloud_name: CLOUDINARY_HOST,
api_key: CLOUDINARY_API,
api_secret: CLOUDINARY_SECRET,
});
const storage = new CloudinaryStorage({
cloudinary: cloudinary,
params: {
folder: "images",
format: async () => "png",
public_id: (req, file) => file.filename,
},
});
const parser = multer({ storage: storage });
module.exports = parser;
Frontend:
SignupBuyer.jsx
const [data, setData] = useState({
proImg: "",
role: "buyer",
firstName: "",
lastName: "",
email: "",
password: "",
cPassword: "",
});
const { proImg, role, firstName, lastName, email, password, cPassword } =
data;
const navigate = useNavigate();
const dispatch = useDispatch();
const { user, isLoading, isSuccess, isError, message } = useSelector(
(state) => state.auth
);
useEffect(() => {
if (isError) toast.error(message);
if (isSuccess || user) {
navigate("/buyerdashboard");
}
dispatch(reset());
}, [user, isError, isSuccess, message, navigate, dispatch]);
//Toggle password display
const [passwordShown, setPasswordShown] = useState(false);
const togglePassword = () => {
setPasswordShown(!passwordShown);
};
const handleImage = (e) => {
setData((prevState) => ({
...prevState,
proImg: e.target.files[0],
}));
console.log(e.target.files[0]);
};
const handleChange = (e) => {
setData((prevState) => ({
...prevState,
[e.target.name]: e.target.value,
}));
};
const handleSubmit = (e) => {
e.preventDefault();
if (password !== cPassword) {
toast.error("Passwords do not match");
} else {
const userData = { proImg, role, firstName, lastName, email, password };
dispatch(register(userData));
}
};
if (isLoading) return <Spinner />;
return (
<>
<div className="signup">
<div className="signup__container">
<h3>Join Us to Hire Talent</h3>
<form
className="signup__container-form"
encType="multipart/form-data"
onSubmit={handleSubmit}
>
<input
type="file"
accept=".png, .jpeg, .jpg"
name="proImg"
onChange={handleImage}
/>
2
Answers
Ok so I fixed the error I was getting and got the expected result. For anyone having a similar problem, I had to change the handleSubmit from the frontend from:
To this:
Since your input type has a name property as proImg, looks like you need to specify req.proImg as shown below: