How to send formData that includes image - node.js

I'm trying to send my form using React frontend to NodeJS backend and I'm not getting any data in formData() object, here is my code for React:
import { useState, useEffect } from 'react'
import Axios from 'axios'
import Notification from '../../../components/Notification'
const DashboardWorkAdd = ({ subTitle = 'works' }) => {
//Form States
const [workName, setWorkName] = useState('')
const [workGithub, setWorkGithub] = useState('')
const [workOnlineLink, setWorkOnlineLink] = useState('')
const [workDate, setWorkDate] = useState('')
const [workDesc, setWorkDesc] = useState('')
const [workAdded, setworkAdded] = useState('')
const [workAddedMsg, setworkAddedMsg] = useState('')
const [workImgFile, setWorkImgFile] = useState('')
const [preview, setPreview] = useState()
const formMsg = document.querySelector('.notification__msg')
const updateWorkImg = (e) => {
const file = e.target.files[0]
if (file) {
const fileType = file.type.split('/')[0]
const fileSizeToMB = file.size / 1000000
const MAX_FILE_SIZE = 1 //mb
if (formMsg) {
formMsg.classList.remove('hidden')
if (fileType !== 'image') {
formMsg.textContent = 'you can add only image file'
} else if (fileSizeToMB > MAX_FILE_SIZE) {
formMsg.textContent = `you can't add more than ${MAX_FILE_SIZE} MB`
return
} else {
formMsg.classList.add('hidden')
formMsg.textContent = ''
setWorkImgFile(file)
}
}
}
}
useEffect(() => {
// if there's an image
if (workImgFile) {
const reader = new FileReader()
reader.onloadend = () => setPreview(reader.result)
reader.readAsDataURL(workImgFile)
} else {
setPreview(null)
}
}, [workImgFile])
const handleAddWork = async (e) => {
e.preventDefault()
//using FormData to send constructed data
const formData = new FormData()
formData.append('workImg', workImgFile)
formData.append('workName', workName)
formData.append('workGithub', workGithub)
formData.append('workOnlineLink', workOnlineLink)
formData.append('workDate', workDate)
formData.append('workDesc', workDesc)
console.log(formData)
if (
(workName === '' || workGithub === '' || workOnlineLink === '' || workDate === '',
workDesc === '')
) {
formMsg.textContent = 'please add all data'
} else {
try {
const response = await Axios.post(
`${
process.env.NODE_ENV === 'development'
? process.env.REACT_APP_API_LOCAL_URL
: process.env.REACT_APP_API_URL
}/workAdd`,
{ workImgFile, workName, workGithub, workOnlineLink, workDate, workDesc }
)
const { workAdded, message } = response.data
setworkAdded(workAdded)
setworkAddedMsg(message)
} catch (err) {
console.error(err)
}
}
}
return (
<>
<h3 className='text-3xl mt-20 mb-12 text-center font-semibold'>{subTitle}</h3>
<div className='h-full'>
<div className='flex flex-col gap-3 py-4 text-sm font-semibold'>
<Notification sendStatus={workAdded} sendStatusMsg={workAddedMsg} />
<div className='notification__msg'></div>
<form
method='POST'
className='flex flex-col gap-14'
encType='multipart/form-data'
onSubmit={handleAddWork}
>
<label
htmlFor='workImg'
className='flex flex-wrap justify-center gap-5 md:justify-between items-center cursor-pointer'
>
<img
src={
preview === null ? 'https://source.unsplash.com/random?webdev' : preview
}
alt='Work Portfolio Preview'
className='w-36 h-36'
/>
<input
type='file'
name='workImg'
id='workImg'
className='bg-blue-500 py-6 px-28 rounded-lg text-white uppercase font-semibold cursor-pointer'
accept='image/*'
onChange={updateWorkImg}
multiple
required
/>
</label>
<div className='dashboard-group'>
<label htmlFor='workName'>work name</label>
<input
type='text'
id='workName'
autoFocus
onChange={(e) => {
setWorkName(e.target.value.trim())
}}
required
/>
</div>
<div className='dashboard-group'>
<label htmlFor='workLinkGithub'>Github Link</label>
<input
type='text'
id='workLinkGithub'
min='5'
max='500'
onChange={(e) => setWorkGithub(e.target.value.trim())}
required
/>
</div>
<div className='dashboard-group'>
<label htmlFor='workLinkOnline'>Online Linl</label>
<input
type='text'
id='workLinkOnline'
min='5'
max='500'
onChange={(e) => setWorkOnlineLink(e.target.value.trim())}
required
/>
</div>
<div className='dashboard-group'>
<label htmlFor='workDate'>Work Date</label>
<input
type='date'
id='workDate'
min='5'
max='500'
onChange={(e) => setWorkDate(e.target.value.trim())}
required
/>
</div>
<div className='dashboard-group'>
<label htmlFor='workDescription'>word description</label>
<textarea
name='workDescription'
id='workDescription'
minLength='10'
maxLength='300'
className=''
onChange={(e) => setWorkDesc(e.target.value.trim())}
required
></textarea>
</div>
<div className='flex justify-around text-lg'>
<button
type='submit'
className='bg-green-500 hover:bg-green-600 py-2 px-20 rounded-lg text-white transition-colors'
>
Add
</button>
</div>
</form>
</div>
</div>
</>
)
}
export default DashboardWorkAdd
Here is my code for NodeJS:
const WorksModel = require('../models/WorkModel')
const { v4: uuidv4 } = require('uuid')
module.exports = async (req, res) => {
const { workName, workGithub, workOnlineLink, workDate, workDesc } = req.body
const { workImg } = req.files
const workImgName = uuidv4() + workImg.name
const workImgMovePath = `${__dirname}/../../client/public/uploads/${workImgName}`
const workImgDisplayPath = `/uploads/${workImgName}`
const works = new WorksModel({
workImgDisplayPath,
workName,
workGithub,
workOnlineLink,
workDate,
workDesc
})
try {
await works.save()
workImg.mv(workImgMovePath, (err) => {
if (err) {
res.send({ message: `sorry, something wrong with the server: ${error}` })
return res.status(500).send(err)
}
})
res.send({
message: 'added succesfully',
workAdded: 1
})
} catch (error) {
res.send({
message: `something went wrong ${error}`,
workAdded: 0
})
}
}
and my WorkModel file is :
const mongoose = require('mongoose')
const WorkSchema = new mongoose.Schema({
workImgDisplayPath: {
type: String,
required: true
},
workName: {
type: String,
required: true
},
workGithub: {
type: String,
required: true
},
workOnlineLink: {
type: String,
required: true
},
workDate: {
type: Date,
required: true,
default: new Date().toLocaleDateString('ar-EG', {
weekday: 'long',
year: 'numeric',
month: 'short',
day: 'numeric'
})
},
workDesc: {
type: String,
required: true
}
})
const WorkModel = mongoose.model('mhmdhidrPortfolio', WorkSchema)
module.exports = WorkModel
the issue I'm having when I get the data in the backend is the req.files are undefined, I googled a lot and I don't know the issue exactly.
Thanks for your help.

I solved my problem, I just had to add:
const fileUpload = require('express-fileupload')
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(fileUpload())
inside my index.js (server) file

Related

Electron React, my form is not preventing default event

I've tried everything, I used the standard input, without success, I already changed the onSubmit to onClick, without success, I already put the function in the onClick button without success, it only works when I remove the input field of type file
I am using Electron React Boilerplate
https://github.com/electron-react-boilerplate/electron-react-boilerplate
import { Button, Pane, FileUploader, FileCard, Alert } from 'evergreen-ui';
import VideoSnapshot from 'video-snapshot';
import Input from 'renderer/components/Input';
import React from 'react';
import axios from 'axios';
import ContainerMenu from '../components/ContainerMenu';
const Admin = () => {
const [name, setName] = React.useState('');
const [genre, setGenre] = React.useState('');
const [path, setPath] = React.useState('');
const [preview, setPreview] = React.useState(null);
const [submitError, setSubmitError] = React.useState('');
const [progress, setProgress] = React.useState(0);
const [files, setFiles] = React.useState([]);
const [fileRejections, setFileRejections] = React.useState([]);
const handleChange = React.useCallback((files) => setFiles([files[0]]), []);
const handleRejected = React.useCallback(
(fileRejections) => setFileRejections([fileRejections[0]]),
[]
);
const handleRemove = React.useCallback(() => {
setFiles([]);
setFileRejections([]);
}, []);
const handleSubmit = async (e) => {
e.preventDefault();
setSubmitError('');
if (files.length === 0) {
setSubmitError('O arquivo não pode ser vazio');
return;
}
try {
const snapshoter = new VideoSnapshot(files[0]);
const previewSrc = await snapshoter.takeSnapshot();
if (previewSrc) setPreview(previewSrc);
} catch (error) {
console.log(error);
}
try {
// const previewData = new FormData();
// const previewObject = {
// preview: previewSrc,
// };
// previewData.append('file', previewObject);
// await axios.post('http://localhost:3000/api/upload', previewData);
const formData = new FormData();
formData.append('file', files[0]);
formData.append('name', name);
formData.append('genre', genre);
formData.append('path', 'videos');
const response = await axios.post(
'http://localhost:3000/api/upload',
formData,
{
onUploadProgress: (progressEvent) => {
const progress = (progressEvent.loaded / progressEvent.total) * 50;
setProgress(progress);
},
}
);
} catch (error) {
console.log(error);
setSubmitError(error.message);
}
};
return (
<div>
<ContainerMenu />
<div className="container">
<form
encType="multpart/form-data"
className="form-group"
onSubmit={handleSubmit}
>
<h2>Enviar Arquivos</h2>
<br />
<div>
<Input
type="text"
label="Nome"
placeholder="Nome ..."
onChange={setName}
value={name}
/>
</div>
<div>
<Input
type="text"
label="Gênero"
placeholder="Gênero ..."
onChange={setGenre}
value={genre}
/>
</div>
<div>
<Pane>
<FileUploader
label="Upload File"
description="You can upload 1 file. File can be up to 50 MB."
maxSizeInBytes={50 * 1024 ** 2}
maxFiles={1}
onChange={handleChange}
onRejected={handleRejected}
renderFile={(file) => {
const { name, size, type } = file;
const fileRejection = fileRejections.find(
(fileRejection) => fileRejection.file === file
);
const { message } = fileRejection || {};
return (
<FileCard
key={name}
isInvalid={fileRejection != null}
name={name}
onRemove={handleRemove}
sizeInBytes={size}
type={type}
validationMessage={message}
/>
);
}}
values={files}
/>
</Pane>
</div>
<div>
<Button
isLoading={progress > 0 && progress < 100}
marginRight={16}
appearance="primary"
type="submit"
>
Enviar {progress.toFixed(2)}%
</Button>
</div>
<br />
<div>{preview && <img src={preview} alt="preview" />}</div>
<br />
<div>
{submitError.length > 0 && (
<Alert intent="danger" title="Error">
{submitError}
</Alert>
)}
</div>
</form>
</div>
</div>
);
};
export default function App() {
return <Admin />;
}

api proplem and redux

i want to send data from client to api then database but i get error in the api that appears in console PUT http://localhost:5000/api/products/find/undefined 500 (Internal Server Error
the error started when i added the firebase part and i think the proplem is the api doesnt recieve ip
the api is
//edit
const updateProduct=async(req,res)=>{
try {
const updatedProduct = await Product.findByIdAndUpdate(
req.params.id,
{
$set: req.body,
},
{ new: true }
);
res.status(200).json(updatedProduct);
} catch (err) {
res.status(500).json(err);
}
}
client side is
import { Link, useLocation } from "react-router-dom";
import "./product.css";
import Chart from "../../components/chart/Chart";
import { Publish } from "#material-ui/icons";
import { useDispatch, useSelector } from "react-redux";
import { useEffect, useMemo, useState } from "react";
import { userRequest } from "../../requestMethods";
import { updateProduct } from "../../redux/apiCalls";
import {
getStorage,
ref,
uploadBytesResumable,
getDownloadURL,
} from "firebase/storage";
import app from "../../firebase";
export default function Product() {
const [user,setUser]=useState({});
const [file, setFile] = useState(null);
const location = useLocation();
const productId = location.pathname.split("/")[2];
const [pStats, setPStats] = useState([]);
const product = useSelector((state) =>
state.product.products.find((product) => product._id === productId)
);
const handleChange =(e)=>{
const value=e.target.value;
setUser({
...user,
[e.target.name]:value
});
};
console.log(user)
const dispatch = useDispatch();
const handleclick=(id)=>{
const fileName = new Date().getTime() + file.name;
const storage = getStorage(app);
const storageRef = ref(storage, fileName);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on(
"state_changed",
(snapshot) => {
// Observe state change events such as progress, pause, and resume
// Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
const progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log("Upload is " + progress + "% done");
switch (snapshot.state) {
case "paused":
console.log("Upload is paused");
break;
case "running":
console.log("Upload is running");
break;
default:
}
},
(error) => {
// Handle unsuccessful uploads
},
() => {
// Handle successful uploads on complete
// For instance, get the download URL: https://firebasestorage.googleapis.com/...
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
const userr = { ...user, img: downloadURL};
updateProduct(id,dispatch,userr)
});
}
);
console.log(id)
}
useEffect(() => {
updateProduct(dispatch);
}, [dispatch]);
const MONTHS = useMemo(
() => [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Agu",
"Sep",
"Oct",
"Nov",
"Dec",
],
[]
);
useEffect(() => {
const getStats = async () => {
try {
const res = await userRequest.get("order/income?pid=" + productId);
const list = res.data.sort((a,b)=>{
return a._id - b._id
})
list.map((item) =>
setPStats((prev) => [
...prev,
{ name: MONTHS[item._id - 1], Sales: item.total },
])
);
} catch (err) {
console.log(err);
}
};
getStats();
}, [productId, MONTHS]);
return (
<div className="product">
<div className="productTitleContainer">
<h1 className="productTitle">Product</h1>
<Link to="/newproduct">
<button className="productAddButton">Create</button>
</Link>
</div>
<div className="productTop">
<div className="productTopLeft">
<Chart data={pStats} dataKey="Sales" title="Sales Performance" />
</div>
<div className="productTopRight">
<div className="productInfoTop">
<img src={product.img} alt="" className="productInfoImg" />
<span className="productName">{product.title}</span>
</div>
<div className="productInfoBottom">
<div className="productInfoItem">
<span className="productInfoKey">id:</span>
<span className="productInfoValue">{product._id}</span>
</div>
<div className="productInfoItem">
<span className="productInfoKey">sales:</span>
<span className="productInfoValue">5123</span>
</div>
<div className="productInfoItem">
<span className="productInfoKey">in stock:</span>
<span className="productInfoValue">{product.inStock}</span>
</div>
</div>
</div>
</div>
<div className="productBottom">
<form className="productForm">
<div className="productFormLeft">
<label>Product Name</label>
<input type="text" placeholder={product.title} name="title" onChange={handleChange}/>
<label>Product Description</label>
<input type="text" placeholder={product.desc} name="desc" onChange={handleChange} />
<label>Price</label>
<input type="text" placeholder={product.price} name="price" onChange={handleChange} />
<label>In Stock</label>
<select name="inStock" id="idStock" onChange={handleChange}>
<option value="true">Yes</option>
<option value="false">No</option>
</select>
</div>
<div className="productFormRight">
<div className="productUpload">
<img src={product.img} alt="" className="productUploadImg" />
<label for="file">
<Publish />
</label>
<input type="file" id="file" style={{ display: "none" }} name="img" onChange={(e) => setFile(e.target.files[0])} />
</div>
<button className="productButton" onClick={(e)=>handleclick(e.preventDefault(),product._id)}>Update</button>
</div>
</form>
</div>
</div>
);
}
the update function
export const updateProduct = async (id, dispatch, product) => {
dispatch(updateProductStart());
try {
const res=await userRequest.put(`/products/find/${id}`,product)
dispatch(updateProductSuccess({ id, product }));
} catch (err) {
dispatch(updateProductFailure());
}
};
update success function
updateProductSuccess: (state, action) => {
state.isFetching = false;
state.products[
state.products.findIndex((item) => item._id === action.payload.id)
] = action.payload.product;
},
You're not calling handleclick with the expected arguments from the button with className "productButton".
Change the line to the following
<button className="productButton" onClick={(e)=> {
e.preventDefault();
handleclick(product._id);
}
}>Update</button>

MongoDb says duplicate error even it does not exist in the collection using Node.js

This is my customer schema.
const mongoose = require('mongoose');
const { isEmail } = require('validator');
const uniqueValidator = require('mongoose-unique-validator');
const bcrypt = require('bcrypt');
const requiredString = {
type: String,
required: true
}
const customerSchema = new mongoose.Schema({
firstname: {
type: String,
required: [true, 'Please enter your first name'],
},
lastname: {
type: String,
required:[true, 'Please enter your last name']
},
username: {
type: String,
unique: true,
required: true,
lowercase:true,
},
email: {
type: String,
unique: true,
validate: [isEmail, 'Please enter a valid email'],
lowercase: true
},
address: requiredString,
mobile: {
type:String,
required:true,
unique:true,
maxlength: [11, 'Minimum length is 11 number']
},
barangay: requiredString,
city: requiredString,
province: requiredString,
postalCode:requiredString,
password: {
type: String,
required: [true, 'Please enter a password'],
minlength: [8, 'Minimum password length is 8 characters']
},
verified: {
type: Boolean
},
status: requiredString,
code: requiredString,
profilePicture: {
type: String
}
});
// validate for uniqueness
customerSchema.plugin(uniqueValidator);
// fire a function before saving to database
customerSchema.pre('save', async function(next) {
const salt = await bcrypt.genSalt();
this.password = await bcrypt.hash(this.password,salt);
next();
})
// create static login method for user
customerSchema.statics.login = async function(username,password) {
const customer = await this.findOne({ username });
if(customer) {
const auth = await bcrypt.compare(password,customer.password);
if(auth) {
return customer;
}
throw Error('Incorrect password');
}
throw Error('This username doesn\'t exist');
}
customerSchema.statics.resetPassword = async function(id,password) {
const customer = await this.findById(id);
if(customer) {
const salt = await bcrypt.genSalt();
const newPassword = await bcrypt.hash(password,salt);
return newPassword;
}
}
const CustomerModel = mongoose.model('customer', customerSchema);
module.exports = CustomerModel;
This is my code in signing up in my controller.js
module.exports.customer_signup_post = async (req, res) => {
const { firstname,lastname,username,email,mobile,address,barangay,city,province,postalCode,password } = req.body;
const verified = false;
const code = Math.floor(Math.random() * 100000);
const status = 'active';
const htmlContent = `
<h1>Hi ${firstname}!</h1>
<h2>${code}</h2>
<p>It seems like you registered with this account. Please use this code to verify your account</p>
<p>Thank you for using Tulin Bicycle Shop! Enjoy Shopping!</p>
`
try {
const newCustomer = await Customer.create({ firstname,lastname,username,email,mobile,address,barangay,city,province,postalCode,password,verified,status,code });
const info = await transporter.sendMail({
from: `'Tulin Bicycle Shop' <${process.env.MAIL_ACCOUNT}>`,
to: `${newCustomer.email}`,
subject: 'Account verification',
html: htmlContent
});
console.log("Message was sent: " + info.response);
res.status(201).json({ mssg: `${newCustomer.firstname} has been created, please check your email for verification`, customerId: newCustomer._id,redirect:`/verify/${newCustomer._id}` });
}
catch(err) {
const errors = handleErrors(err);
res.status(400).json({ errors });
}
}
This is the error being shown when I try to signup new user
E11000 duplicate key error collection: capstone.customers index: username_1 dup key: { username: "neknek" }
This is my code in React.js
import { AiOutlineLoading3Quarters } from 'react-icons/ai'
import { Helmet } from 'react-helmet';
import { useState } from 'react';
import { useNavigate,Link } from 'react-router-dom';
import axios from 'axios';
import StepOne from '../../../components/shop/signup.jsx/StepOne';
import StepTwo from '../../../components/shop/signup.jsx/StepTwo';
import StepThree from '../../../components/shop/signup.jsx/StepThree';
const Signup = () => {
const [firstname,setFirstname] = useState('');
const [lastname,setLastname] = useState('')
const [username,setUsername] = useState('');
const [email,setEmail] = useState('');
const [mobile,setMobile] = useState('');
const [address,setAddress] = useState('');
const [barangay,setBarangay] = useState('');
const [city,setCity] = useState('');
const [province,setProvince] = useState('');
const [postalCode,setPostalCode] = useState('');
const [password,setPassword] = useState('');
const [confirmPassword,setConfirmPassword] = useState('');
const [success,setSuccess] = useState('');
const [isLoading,setIsLoading] = useState(false);
const [passErr,setPassErr] = useState('');
const [usernameErr,setUsernameErr] = useState('');
const [emailErr,setEmailErr] = useState('');
const [mobileErr,setMobileErr] = useState('');
const [passLimitErr,setPassLimitErr] = useState('');
const [activeStep,setActiveStep] = useState('step one');
const navigate = useNavigate();
const onSignup = (e) => {
e.preventDefault();
setIsLoading(true);
if(password !== confirmPassword) {
setPassErr('Password doesn\'t match, please check your password');
setTimeout(() => {
setPassErr('');
},2000);
} else {
axios.post('/customer',{ firstname,lastname,username,email,mobile,address,barangay,city,province,postalCode,password })
.then((data) => {
setSuccess(data.data.mssg);
setTimeout(() => {
navigate(data.data.redirect);
},2000)
setIsLoading(false);
}).catch((err) => {
setEmailErr(err.response.data.errors.email);
setPassLimitErr(err.response.data.errors.password);
setUsernameErr(err.response.data.errors.username);
setMobileErr(err.response.data.errors.mobile);
setTimeout(() => {
setEmailErr('');
setUsernameErr('');
setPassLimitErr('');
setMobileErr('');
},2000)
setIsLoading(false);
})
}
}
return (
<>
<Helmet><title>Tulin Bicycle Shop | Signup</title></Helmet>
<div className="content signup-bg h-full overflow-hidden">
<div className="max-content flex items-center justify-center w-1/2">
<img className="object-cover h-4/5" src="/image/bike-bg.png" alt="Bike background" />
<div className="bg-white h-4/5 w-2/5 relative" onSubmit={onSignup}>
<div className="absolute flex items-center gap-2 top-0 right-0 p-4">
<h2>Already have an account?</h2>
<Link to='/login' className="rounded-full border border-gray-700 p-2 shadow-xl">Sign in</Link>
</div>
<div className="px-8 py-24">
<h1 className="text-gray-800 text-5xl">Sign up</h1>
<span>Create your account</span>
{ isLoading && <h2 className="text-sm text-green-500 flex items-center gap-2"><AiOutlineLoading3Quarters className="animate-spin" />Please wait...</h2> }
{ emailErr && <h2 className="text-sm text-red-500">{ emailErr }</h2> }
{ usernameErr && <h2 className="text-sm text-red-500">{ usernameErr }</h2> }
{ mobileErr && <h2 className="text-sm text-red-500">{ mobileErr }</h2> }
<p className="text-sm text-green-500">{ success }</p>
<div className="flex items-center justify-center gap-2">
{/* Pages button */}
<button onClick={() => setActiveStep('step one')} className={`rounded-full w-5 h-5 border border-gray-700 flex justify-center items-center p-1 text-xs ${activeStep === 'step one' && 'bg-gray-300'}`}>1</button>
<button onClick={() => setActiveStep('step two')} className={`rounded-full w-5 h-5 border border-gray-700 flex justify-center items-center p-1 text-xs ${activeStep === 'step two' && 'bg-gray-300'}`}>2</button>
<button onClick={() => setActiveStep('step three')} className={`rounded-full w-5 h-5 border border-gray-700 flex justify-center items-center p-1 text-xs ${activeStep === 'step three' && 'bg-gray-300'}`}>3</button>
</div>
<form className="flex flex-col gap-2 mt-1" onSubmit={onSignup}>
{/* First Step */}
{ activeStep === 'step one' &&
<StepOne
firstname={firstname}
setFirstname={setFirstname}
lastname={lastname}
setLastname={setLastname}
username={username}
setUsername={setUsername}
email={email}
setEmail={setEmail}
usernameErr={usernameErr}
emailErr={emailErr}
setActiveStep={setActiveStep}
/> }
{/* Second Step */}
{ activeStep === 'step two' &&
<StepTwo
mobile={mobile}
address={address}
barangay={barangay}
city={city}
province={province}
postalCode={postalCode}
setMobile={setMobile}
setAddress={setAddress}
setBarangay={setBarangay}
setCity={setCity}
setProvince={setProvince}
setPostalCode={setPostalCode}
setActiveStep={setActiveStep}
/> }
{/* Step Three */}
{ activeStep === 'step three' &&
<StepThree
password={password}
setPassword={setPassword}
confirmPassword={confirmPassword}
setConfirmPassword={setConfirmPassword}
passLimitErr={passLimitErr}
passErr={passErr}
setActiveStep={setActiveStep}
/> }
</form>
</div>
</div>
</div>
</div>
</>
);
};
export default Signup;
The main problem is it always shows that error when I try to signup new users, it will display that error but will be inserted the data anyway. I want to fix this problem and I tried to drop indexes but still not working.
I finally figured it out, the problem is in the frontend part. The form tag sends a data when you're using a button inside it, what I did was created an onClick event in the signup button then call the function onSignup in my StepThree component.
const StepThree = ({ password,setPassword,confirmPassword,setConfirmPassword,passLimitErr,passErr,setActiveStep,onSignup }) => {
return (
<div className="w-full">
<div className="flex flex-col gap-2">
<label htmlFor="password">Password:</label>
<input className="user-auth" type="password" placeholder="Password"
onChange={(e) => setPassword(e.target.value)}
value={password}
required
/>
<span className="pass-error">{ passLimitErr }</span>
</div>
<div className="flex flex-col gap-2">
<label htmlFor="confirm password">Confirm Password:</label>
<input className="user-auth" type="password" placeholder="Confirm password"
onChange={(e) => setConfirmPassword(e.target.value)}
value={confirmPassword}
required
/>
<span className="pass-error">{ passErr }</span>
</div>
<div className="flex items-center gap-2">
<div onClick={() => setActiveStep('step two')} className="flex-col flex gap-2 text-center cursor-pointer">
<span className="bg-gray-900 text-gray-100 p-2 rounded">Previous</span>
</div>
<div className="flex-col flex gap-2">
<button onClick={onSignup} className="signup-btn-user">Signup</button>
</div>
</div>
</div>
)
}
export default StepThree

how to solve this Cannot POST error in MERN?

I am inserting two images along with the form data into MongoDB database.
While both images are stored in my pc folder but all form data isn't uploading in the database.
error
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot POST /</pre>
</body>
</html>
in my console. Please help me how to solve that.
I have tried some previously asked question in stackOF.
app.js
const express = require("express")
const app = express()
const cors = require('cors');
const env = require("dotenv")
const port = 5000
env.config({path: "../server/config.env"})
require("../server/db/conn")
app.use(cors());
app.use(express.json())
app.use(require("../server/router/auth"))
app.listen(port, (err) => {
if (err) {
return console.error(err);
}
return console.log(`server is listening on ${port}`);
});
module.exports = "conn"
register.js(frontend)
const Register = () => {
const [newUser, setNewUser] = useState({
school: "",
address: "",
photo: "",
photoone: ""
});
const handleSubmit = (e) => {
e.preventDefault();
const formData = new FormData();
formData.append("school", newUser.school);
formData.append("photo", newUser.photo);
formData.append("photoone", newUser.photoone)
formData.append("address", newUser.address);
axios({
method: "post",
url: "/teacher",
data: formData,
headers: { "Content-Type": "multipart/form-data" },
})
.then((response) => {
console.log(response)
}).then((data) => {
console.log(data)
}).catch((error) => {
if (error.response) {
console.log(error.response.data)
}
})
};
const handleChange = (e) => {
setNewUser({ ...newUser, [e.target.name]: e.target.value });
};
const handlePhoto = (e) => {
setNewUser({ ...newUser, photo: e.target.files[0] });
};
const handlePhotoone = (e) => {
setNewUser({ ...newUser, photoone: e.target.files[0] });
};
return (
<>
<div className="container main">
<div className="row">
<div className="col-sm-6 col-md-6 col-lg-6">
<form onSubmit={handleSubmit} encType="multipart/form-data">
<div class="mb-3">
<label class="form-label">
Your school
</label>
<input
type="text"
class="form-control"
id="exampleInputPassword1"
id="school"
name="school"
value={newUser.school}
onChange={handleChange}
/>
</div>
<div class="input-group mb-3">
<input
type="file"
id="pic"
accept=".png, .jpg, .jpeg"
name="photo"
onChange={handlePhoto} type="file" class="form-control" id="inputGroupFile02" />
</div>
<div class="input-group mb-3">
<input
type="file"
id="pic"
placeholder="second photo"
accept=".png, .jpg, .jpeg"
name="photoone"
onChange={handlePhotoone} type="file" class="form-control" id="inputGroupFile01" />
</div>
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">
your address
</label>
<input
type="text"
id="address"
name="address"
value={newUser.address}
onChange={handleChange}
class="form-control"
aria-describedby="emailHelp"
/>
</div>
<button
value="register"
type="submit"
class="btn btn-primary"
>
Submit
</button>
</form>
</div>
</div>
</div>
</>
);
};
auth.js(backend)
const mongoose = require("mongoose")
const express = require("express")
const router = express()
require("../db/conn")
const User = require("../model/userSchema")
const Teacher = require("../model/userSchemaTeacher")
const multer = require('multer');
let path = require('path');
let fs = require("fs-extra");
const storage = multer.diskStorage({
destination: function (req, file, cb) {
let schoolname = req.body.school;
let path = `C:/Users/kumar/Desktop/mern/server/images/${schoolname}`;
fs.mkdirsSync(path);
cb(null, path);
// cb(null, 'images');
},
filename: function (req, file, cb) {
cb(null, file.originalname);
}
});
const fileFilter = (req, file, cb) => {
const allowedFileTypes = ['image/jpeg', 'image/jpg', 'image/png'];
if (allowedFileTypes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(null, false);
}
}
let upload = multer({ storage, fileFilter });
router.route('/teacher').post(upload.fields([{
name: "photo", maxCount: 1
}, {
name: "photoone", maxCount: 1
}
])), (req, res) => {
const school = req.body.school;
const photo = req.file.filename
const photoone = req.file.filename
const address = req.body.address;
const newUserData = {
school,
photo,
photoone,
address,
}
const newUser = new Teacher(newUserData);
newUser.save()
.then(() => res.json('User Added'))
.catch((err) => {
console.log(err);
});
}
Please see how to solve that?
The route you are trying to POST your form data is not defined please set your route path like this:
router.post('/teacher',upload.fields([{
name: "photo", maxCount: 1
}, {
name: "photoone", maxCount: 1
}
]), (req, res) => {
const school = req.body.school;
const photo = req.files['photo'][0]
const photoone = req.files['photoone'][0]
const address = req.body.address;
const newUserData = {
school,
photo,
photoone,
address,
}
const newUser = new Teacher(newUserData);
newUser.save()
.then(() => res.json('User Added'))
.catch((err) => {
console.log(err);
});
})
...

Unable to fetch data from Form using FormData

I am creating an 'edit profile' page for a dashboard the technologies that I use for the same are Next.js, Node.js & MongoDB.
Note: skip to the backend part if you just wanted to know the issue.
Frontend
Firstly,let me explain the Frontend part.
I am using useRef() inorder to reference data(name,bio) in the inputfields. which are working nicely.
Everything is fine the issue is in the handlesbumit() event_handler.
I am using FormData to send my form data to the backend API
If you're thinking why I'm not using a usual body object to send data the reason is that I have to add the profile picture updation later for which I have to send files , which as far I know we can't do that with an Object and yeah just to inform you it works fine if I would have used that Object part but can't use it with profilepicture updation.
The value that I have consoled out for the references are all good, and the rest of the handler is just as it is written can't find anything odd in that.
import { useUser } from '../../../lib/hooks';
import React, { useState, useEffect, useRef } from 'react';
import Head from 'next/head';
import { ImBook, ImListNumbered } from 'react-icons/im';
import { AiFillGithub, AiOutlineTwitter, AiFillFacebook, AiFillInstagram, AiFillLinkedin } from 'react-icons/ai'
import { FaFacebook, FaStackOverflow } from 'react-icons/fa';
const ProfileSection = () => {
const [user, { mutate }] = useUser();
const [isUpdating, setIsUpdating] = useState(false);
const nameRef = useRef();
const profilePictureRef = useRef();
const bioRef = useRef();
const [msg, setMsg] = useState({ message: '', isError: false });
useEffect(() => {
nameRef.current.value = user.name;
bioRef.current.value = user.Bio;
}, [user]);
const handleSubmit = async (event) => {
event.preventDefault();
if (isUpdating) return;
setIsUpdating(true);
console.log(nameRef.current.value); //Testing
console.log(bioRef.current.value); //Testing
const formData = new FormData();
formData.append('name', nameRef.current.value);
formData.append('Bio', bioRef.current.value);
console.log(formData.get('name'));
const res = await fetch('/api/user', {
method: 'PATCH',
body: formData,
});
if (res.status === 200) {
const userData = await res.json();
mutate({
user: {
...user,
...userData.user,
},
});
setMsg({ message: 'Profile updated' });
} else {
setMsg({ message: await res.text(), isError: true });
}
};
return (
<>
<Head>
<title>Settings</title>
</Head>
<main>
<div class="row">
<div class="col s12 m12">
<div className="card-panel br-10">
{msg.message ? <p style={{ color: msg.isError ? 'red' : '#0070f3', textAlign: 'center' }}>{msg.message}</p> : null}
<form onSubmit={handleSubmit}>
<div className="row">
<div className="col s12 m6 l6">
<label htmlFor="name">
Name
<input
required
id="name"
name="name"
type="text"
ref={nameRef}
/>
</label>
</div>
<div className="col s12 m6 l6">
<label htmlFor="bio">
Bio
<textarea
id="bio"
name="bio"
type="text"
ref={bioRef}
/>
</label>
</div>
</div>
<div className="center-align">
<button disabled={isUpdating} className="btn" type="submit" >Save</button>
</div>
</form>
</div>
</div>
</div>
</main>
</>
);
};
const SettingPage = () => {
const [user] = useUser();
if (!user) {
return (
<>
<p>Please sign in</p>
</>
);
}
return (
<>
<ProfileSection />
</>
);
};
export default SettingPage;
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
import { useUser } from '../../../lib/hooks';
import React, { useState, useEffect, useRef } from 'react';
import Head from 'next/head';
import { ImBook, ImListNumbered } from 'react-icons/im';
import { AiFillGithub, AiOutlineTwitter, AiFillFacebook, AiFillInstagram, AiFillLinkedin } from 'react-icons/ai'
import { FaFacebook, FaStackOverflow } from 'react-icons/fa';
const ProfileSection = () => {
const [user, { mutate }] = useUser();
const [isUpdating, setIsUpdating] = useState(false);
const nameRef = useRef();
const profilePictureRef = useRef();
const bioRef = useRef();
const [msg, setMsg] = useState({ message: '', isError: false });
useEffect(() => {
nameRef.current.value = user.name;
bioRef.current.value = user.Bio;
}, [user]);
const handleSubmit = async (event) => {
event.preventDefault();
if (isUpdating) return;
setIsUpdating(true);
console.log(nameRef.current.value);
console.log(bioRef.current.value);
const formData = new FormData();
formData.append('name', nameRef.current.value);
formData.append('Bio', bioRef.current.value);
console.log(formData.get('name'));
const res = await fetch('/api/user', {
method: 'PATCH',
body: formData,
});
if (res.status === 200) {
const userData = await res.json();
mutate({
user: {
...user,
...userData.user,
},
});
setMsg({ message: 'Profile updated' });
} else {
setMsg({ message: await res.text(), isError: true });
}
};
return (
<>
<Head>
<title>Settings</title>
</Head>
<main>
<div class="row">
<div class="col s12 m12">
<div className="card-panel br-10">
{msg.message ? <p style={{ color: msg.isError ? 'red' : '#0070f3', textAlign: 'center' }}>{msg.message}</p> : null}
<form onSubmit={handleSubmit}>
<div className="row">
<div className="col s12 m6 l6">
<label htmlFor="name">
Name
<input
required
id="name"
name="name"
type="text"
ref={nameRef}
/>
</label>
</div>
<div className="col s12 m6 l6">
<label htmlFor="bio">
Bio
<textarea
id="bio"
name="bio"
type="text"
ref={bioRef}
/>
</label>
</div>
</div>
<div className="center-align">
<button disabled={isUpdating} className="btn" type="submit" >Save</button>
</div>
</form>
</div>
</div>
</div>
</main>
</>
);
};
const SettingPage = () => {
const [user] = useUser();
if (!user) {
return (
<>
<p>Please sign in</p>
</>
);
}
return (
<>
<ProfileSection />
</>
);
};
export default SettingPage;
Backend
Now, the backend API for the same handlesubmit() event_handler i.e. 'api/user'
Please ignore the handler, it's just a predefined middleware npm next-connect which itself checks what type of request is coming if its 'PATCH' it will run handler.patch.
The Issue is the value of name & Bio is undefined,which means its not getting values from req.body;
And to check I also consoled out req.body which give out this
The data is correct but req.body is not a Object its a String now and I get it, its because I'm using formdata so how to get the values of name & Bio from this req.body ?
import nextConnect from 'next-connect';
import middleware from '../../../middlewares/middleware';
import { extractUser } from '../../../lib/api-helpers';
const handler = nextConnect();
handler.use(middleware);
handler.get(async (req, res) => res.json({ user: extractUser(req) }));
handler.patch(async (req, res) => {
if (!req.user) {
req.status(401).end();
return;
}
const { name, Bio } = req.body;
await req.db.collection('users').updateOne(
{ _id: req.user._id },
{
$set: {
name:name,
Bio: Bio,
},
},
);
res.json({ user: { name, Bio } });
});
export default handler;
I have encountered a this issue.
I was resolve it by use 2 form, a form use to get user's info as email, password and the other for send user's picture.
Maybe has best practice for this case.

Resources