I'm encountering difficulties while trying to post form data, including file uploads, to MongoDB using Multer and Express. Although the files are successfully uploaded to Firebase Storage, the form data is not being posted to MongoDB. Additionally, my MongoDB connection seems to be established without any issues. Could anyone provide insights into potential solutions or debugging steps to resolve this issue? Any help would be greatly appreciated.
User Model:
User
+-----------------------------------------+
| User |
+-----------------------------------------+
| _id: ObjectId |
| firstname: String (required, min: 2, |
| max: 50) |
| lastname: String (required, min: 2, |
| max: 50) |
| email: String (required, max: 50, |
| unique: true) |
| password: String (required, min: 6) |
| picturepath: String (default: "") |
| friends: Array (default: []) |
| location: String |
| occupation: String |
| viewedprofile: Number |
| impression: Number |
| createdAt: Date |
| updatedAt: Date |
+-----------------------------------------+
index.js (Server):
const __filename=fileURLToPath(import.meta.url);
const __dirname= path.dirname(__filename);
dotenv.config();
const app=express();
app.use(express.json())
app.use(helmet());
app.use(helmet.crossOriginResourcePolicy({policy:"cross-origin"}));
app.use(morgan("common"));
app.use(bodyParser.json({limit:"30mb",extended:true}));
app.use(bodyParser.urlencoded({limit:"30mb",extended:true}));
app.use(cors());
app.use("/assets",express.static(path.join(__dirname,'public/assets')));
/* File Storage */
const Storage = multer.memoryStorage(); // Use memory storage for handling files in memory
const upload = multer({ storage: Storage });
/*Routes With Files*/
app.post("/auth/register", upload.single("picture"), async (req, res) => {
try {
if (!req.file) {
return res.status(400).json({erro:'No files were uploaded.'});
}
const bucket = firebaseStorage;
const file = bucket.file(req.file.originalname);
const metadata = {
metadata: {
contentType: req.file.mimetype,
},
};
await file.save(req.file.buffer, metadata);
const [url] = await file.getSignedUrl({ action: 'read', expires: '01-01-2100' });
const picturepath = url;
res.status(201).json({ picturepath });
} catch (error) {
console.error('Error uploading file to Firebase Storage:', error);
res.status(500).json({ error: 'Internal server error' });
}
},register);
/*Routes */
app.use("/auth",authRoutes);
app.use("/users",userRoutes);
app.use("/posts",postRoutes)
/*MONGOOSE SETUP */
const PORT=process.env.PORT || 6001;
mongoose.connect(process.env.MONGO_URL)
.then(()=>{
app.listen(PORT,()=>console.log(`Server Started`));
// User.insertMany(users);
// Post.insertMany(posts);
})
.catch((error)=>console.log(`${error} did not connect`));
Form.jsx (frontend):
import { useState } from "react";
import {
Box,
Button,
TextField,
useMediaQuery,
Typography,
useTheme,
} from "@mui/material";
import EditOutlinedIcon from "@mui/icons-material/EditOutlined";
import { Formik } from "formik";
import * as yup from "yup";
import { useNavigate } from "react-router-dom";
import { useDispatch } from "react-redux";
import { setLogin } from "../../state";
import Dropzone from "react-dropzone";
import FlexBetween from "../../components/FlexBetween";
import { ref, uploadBytes, getDownloadURL } from "firebase/storage";
import { imageDB } from "../../firebaseConfig";
const registerSchema = yup.object().shape({
firstname: yup.string().required("required"),
lastname: yup.string().required("required"),
email: yup.string().email("invalid email").required("required"),
password: yup.string().required("required"),
location: yup.string().required("required"),
occupation: yup.string().required("required"),
picture: yup.string().required("required"),
});
const loginSchema = yup.object().shape({
email: yup.string().email("invalid email").required("required"),
password: yup.string().required("required"),
});
const initialValuesRegister = {
firstname: "",
lastname: "",
email: "",
password: "",
location: "",
occupation: "",
picture: "",
};
const initialValuesLogin = {
email: "",
password: "",
};
const Form = () => {
const [pageType, setPageType] = useState("login");
const [alertMessage, setAlertMessage] = useState("");
const { palette } = useTheme();
const dispatch = useDispatch();
const navigate = useNavigate();
const isNonMobile = useMediaQuery("(min-width:600px)");
const isLogin = pageType === "login";
const isRegister = pageType === "register";
const handleFileUpload = async (file) => {
try {
if (!file) {
throw new Error("File is undefined or null");
}
const storageRef = ref(imageDB); // Use imageDb instead of storage
const fileRef = ref(storageRef, `Users/${file.name}`);
await uploadBytes(fileRef, file);
const downloadURL = await getDownloadURL(fileRef);
console.log(downloadURL);
return downloadURL;
} catch (error) {
console.error("Error uploading image:", error);
throw error;
}
};
const register = async (values, onSubmitProps) => {
try {
values.email = values.email.toLowerCase();
const imageURL = await handleFileUpload(values.picture);
const formData = new FormData();
for (let key in values) {
formData.append(key, values[key]);
}
formData.append("picturepath", imageURL);
for (const entry of formData.entries()) {
console.log(entry);
}
const savedUserResponse = await fetch(
"http://localhost:3001/auth/register",
{
method: "POST",
body: formData,
}
);
const savedUser = await savedUserResponse.json();
console.log("SAVED USER:", savedUser.picturepath);
if (savedUser.error) {
console.log(savedUser.msg);
setAlertMessage(savedUser.msg);
} else {
onSubmitProps.resetForm();
setPageType("login");
}
} catch (error) {
console.error("Error registering:", error);
setAlertMessage(error.message || "Failed to register");
}
};
const login = async (values, onSubmitProps) => {
const loggedInResponse = await fetch("http://localhost:3001/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(values),
});
const loggedIn = await loggedInResponse.json();
onSubmitProps.resetForm();
if (loggedIn.msg) {
setAlertMessage(loggedIn.msg);
} else {
dispatch(
setLogin({
user: loggedIn.user,
token: loggedIn.token,
})
);
navigate("/home");
}
};
const handleFormSubmit = async (values, onSubmitProps) => {
if (isLogin) await login(values, onSubmitProps);
if (isRegister) await register(values, onSubmitProps);
};
return (
<Formik
onSubmit={handleFormSubmit}
initialValues={isLogin ? initialValuesLogin : initialValuesRegister}
validationSchema={isLogin ? loginSchema : registerSchema}
>
{({
values,
errors,
touched,
handleBlur,
handleChange,
handleSubmit,
setFieldValue,
resetForm,
}) => (
<form onSubmit={handleSubmit}>
<Box
display="grid"
gap="30px"
gridTemplateColumns="repeat(4, minmax(0, 1fr))"
sx={{
"& > div": { gridColumn: isNonMobile ? undefined : "span 4" },
}}
>
{isRegister && (
<>
<TextField
label="First Name"
onBlur={handleBlur}
onChange={handleChange}
value={values.firstname}
name="firstname"
error={
Boolean(touched.firstname) && Boolean(errors.firstname)
}
helperText={touched.firstname && errors.firstname}
sx={{ gridColumn: "span 2" }}
/>
<TextField
label="Last Name"
onBlur={handleBlur}
onChange={handleChange}
value={values.lastname}
name="lastname"
error={Boolean(touched.lastname) && Boolean(errors.lastname)}
helperText={touched.lastname && errors.lastname}
sx={{ gridColumn: "span 2" }}
/>
<TextField
label="Location"
onBlur={handleBlur}
onChange={handleChange}
value={values.location}
name="location"
error={Boolean(touched.location) && Boolean(errors.location)}
helperText={touched.location && errors.location}
sx={{ gridColumn: "span 4" }}
/>
<TextField
label="Occupation"
onBlur={handleBlur}
onChange={handleChange}
value={values.occupation}
name="occupation"
error={
Boolean(touched.occupation) && Boolean(errors.occupation)
}
helperText={touched.occupation && errors.occupation}
sx={{ gridColumn: "span 4" }}
/>
<Box
gridColumn="span 4"
border={`1px solid ${palette.neutral.medium}`}
borderRadius="5px"
p="1rem"
>
<Dropzone
acceptedFiles=".jpg,.jpeg,.png"
multiple={false}
onDrop={(acceptedFiles) =>
setFieldValue("picture", acceptedFiles[0])
}
>
{({ getRootProps, getInputProps }) => (
<Box
{...getRootProps()}
border={`2px dashed ${palette.primary.main}`}
p="1rem"
sx={{ "&:hover": { cursor: "pointer" } }}
>
<input {...getInputProps()} />
{!values.picture ? (
<p>Add Picture Here</p>
) : (
<FlexBetween>
<Typography>{values.picture.name}</Typography>
<EditOutlinedIcon />
</FlexBetween>
)}
</Box>
)}
</Dropzone>
</Box>
</>
)}
<TextField
label="Email"
onBlur={handleBlur}
onChange={handleChange}
value={values.email}
name="email"
error={Boolean(touched.email) && Boolean(errors.email)}
helperText={touched.email && errors.email}
sx={{ gridColumn: "span 4" }}
/>
<TextField
label="Password"
type="password"
onBlur={handleBlur}
onChange={handleChange}
value={values.password}
name="password"
error={Boolean(touched.password) && Boolean(errors.password)}
helperText={touched.password && errors.password}
sx={{ gridColumn: "span 4" }}
/>
</Box>
{/* BUTTONS */}
<Box>
{alertMessage && (
<Typography
variant="body2"
sx={{ color: "red", mb: 2 , mt:1}}
>{alertMessage}</Typography>
)}
<Button
fullWidth
type="submit"
sx={{
m: "2rem 0",
p: "1rem",
backgroundColor: palette.primary.main,
color: palette.background.alt,
"&:hover": { color: palette.primary.main },
}}
>
{isLogin ? "LOGIN" : "REGISTER"}
</Button>
<Typography
onClick={() => {
setPageType(isLogin ? "register" : "login");
resetForm();
}}
sx={{
textDecoration: "underline",
color: palette.primary.main,
"&:hover": {
cursor: "pointer",
color: palette.primary.light,
},
}}
>
{isLogin
? "Don't have an account? Sign Up here."
: "Already have an account? Login here."}
</Typography>
</Box>
</form>
)}
</Formik>
);
};
export default Form;
I implemented a form using React and used the react-dropzone library for file uploads. Upon form submission, I expected the file is been uploaded to file storage and also getting picturepath as imageurl.
Not getting any error in terminal as well as console but unable to see entry in db.
Please check my register function .
Looking at your user model and assuming you're using a more recent version of mongoose. Something you need to know before using MongoDB or mongoose, you should remember that, it doesn't require you adding the
_idon the model because at runtime it automatically adds it to every new document that is created. You only add it unless if you want to override it with your own unique value. You must be very careful doing that. As for thecreatedAtandupdatedAtyou also don't need that, simply because if you enable thetimestamps: trueproperty, mongoose automatically adds the timestamps to every document you create and also updates the time when a document is updated. I have helped you refactored your User model. That's the first place to begin with for your debuggingYour model should look like this