Unable to Post Form Data to MongoDB because of picturepath

23 views Asked by At

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 .

1

There are 1 answers

1
Arekhandia Harry On

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 _id on 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 the createdAt and updatedAt you also don't need that, simply because if you enable the timestamps: true property, 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 debugging

Your model should look like this

import {Schema, model } from 'mongoose';

  const userSchema = new Schema({
    firstname: {
      type: String,
      required: true,
      minLength: 2,
      maxLength: 50
    },

    lastname: {
      type: String,
      required: true,
      minLength: 2,
      maxLength: 50
    },

    email: {
      type: String,
      maxLength: 50,
      unique: true,
      required: true
    },

    password: {
      type: String,
      minLength: 6,
      required: true
    },

    picturepath:{
      type: String,
      default: '' // There's no point making a default value if it'll be null or empty string
    },

    friends: [],
    location: String,
    occupation: String,
    viewedprofile: Number,
    impression: Number
  },
  {timestamps: true});
    
  const User = mongoose.model('User', userSchema);
  module.exports = User;