Search code examples
javascriptnode.jsexpressmultersharp

Facing Issues Sending FormData with 'multipart/form-data' In fetch API


I've this form with pug code

form.form.form-user-data
 .form__group
   label.form__label(for='name') Name
   input#name.form__input(type='text', value=`${user.name}`, required)
 .form__group.ma-bt-md
   label.form__label(for='email') Email address
   input#email.form__input(type='email', value=`${user.email}`, required)
 .form__group.form__photo-upload
   img.form__user-photo(src=`/img/users/${user.photo}`, alt=`Photo of${user.name}`)
   input.form__upload(type='file' accept='image/*' id='photo' name='photo' )
   label(for='photo') Choose new photo

This is the Backend with Node&Epress

const multerStorage = multer.memoryStorage();

const multerFitler = (req, file, cb) => {
  if (file.mimetype.startsWith('image')) {
    cb(null, true);
  } else cb(new AppError('Not an image! Please upload an image.', 400));
};
const upload = multer({
  storage: multerStorage,
  fileFilter: multerFitler,
});
exports.uploadUserPhoto = upload.single('photo');

exports.resizeUserPhoto = (req, res, next) => {
  if (!req.file) return next();

  req.file.filename = `user-${req.user._id}-${Date.now()}.jpeg`;

  // console.log(req.file);

  sharp(req.file.buffer)
    .resize(500, 500)
    .toFormat('jpeg')
    .jpeg({ quality: 90 })
    .toFile(`public/img/users/${req.file.filename}`);

  next();
};
function filterObj(obj, ...allowedFields) {
  const newObj = {};

  Object.keys(obj).forEach((key) => {
    if (allowedFields.includes(key)) newObj[key] = obj[key];
  });

  return newObj;
}
exports.updateMe = catchAsync(async (req, res, next) => {
  if (req.body.password || req.body.passwrodConfirm)
    return next(
      new AppError(
        'This route is not for password updates. Please use /updatePassword route',
        400,
      ),
    );

  const filterdBody = filterObj(req.body, 'name', 'email');
  if (req.file) filterdBody.photo = req.file.filename;

  const updatedUser = await User.findByIdAndUpdate(req.user._id, filterdBody, {
    new: true,
    runValidators: true,
  });

  res.status(200).json({
    status: 'success',
    data: {
      user: updatedUser,
    },
  });
});

This is my route for handling the request

router.patch('/updateMe', uploadUserPhoto, resizeUserPhoto, updateMe);

This is my Frontend code

async function updateData(endpoint, body) {
  const res = await fetch(`http://127.0.0.1:3000/api/v1/users/${endpoint}`, {
    method: 'PATCH',
    body,
  });
  const data = await res.json();

  console.log(data);
}

updateDataForm.addEventListener('submit', (e) => {
    e.preventDefault();
    const form = new FormData(updateDataForm);
    form.append('name', updateNameInput.value);
    form.append('email', updateEmailInput.value);
    form.append('photo', updatePhotoInput.files[0]);

    updateData('updateMe', form);
});

The console.log(data) Gives me this error

{ code: "LIMIT_UNEXPECTED_FILE" field: "photo" message: "Unexpected field" name: "MulterError" }

This error comes from the browser but Postman works very fine

I tried to add headers to the fetch options object

headers: { 'Content-Type': 'multipart/form-data' }

but this is another error which is

{
message: 'Multipart: Boundary not found'
}

In Postman 'multipart/form-data; boundary=calculated when request is sent'

It's just the error happens when the URL is requested from the browser I've asked Chatgpt but nothing


Solution

  • //Why are you adding 'updateDataForm' in FormData try after removing it.
    async function updateData(endpoint, body) {
          const res = await fetch(`http://127.0.0.1:3000/api/v1/users/${endpoint}`, {
            method: 'PATCH',
            body,
          });
          const data = await res.json();
        
          console.log(data);
        }
        
    
    updateDataForm.addEventListener('submit', (e) => {
            e.preventDefault();
            const form = new FormData();
            form.append('name', updateNameInput.value);
            form.append('email', updateEmailInput.value);
            form.append('photo', updatePhotoInput.files[0]);
        
            updateData('updateMe', form);
        });