FormData sent with React.js becomes empty when going to Express.js backend

Issue

I was creating a book library management CRUD and I am Using React v18.
I am struggling to add data to my mongdb atlas because whenever I pass formData to axios.post via Redux, I’m always getting empty req.body on the backend controller.

This however does not occur when I am using postman when adding data to my database via form-data. My login form which also using axios.post works fine and the booklist using axios.get also works fine, the crud for book is the only one that is not working.

This is my bookCreate.js:

const BookCreate = () => {
    const [title, setTitle] = useState('')
    const [responsibility, setResponsibility] = useState('')
    const [uniform_title, setUniform_title] = useState('')
    const [parallel_title, setParallel_title] = useState('')
    const [main_author, setMain_author] = useState('')
    const [other_author, setOther_author] = useState('')
    const [contributors, setContributors] = useState('')
    const [corp_author, setCorp_author] = useState('')
    const [placePub, setPlacePub] = useState('')
    const [publisher, setPublisher] = useState('')
    const [yearPub, setYearPub] = useState('')
    ...
    const submitHandler = (e) => {
        e.preventDefault();
        const formData = new FormData();
        formData.set('title', title);
        formData.set('responsibility', responsibility);
        formData.set('uniform_title', uniform_title);
        formData.set('parallel_title', parallel_title);
        formData.set('main_author', main_author);
        formData.set('other_author', other_author);
        formData.set('contributors', contributors);
        formData.set('corp_author', corp_author);
        formData.set('placePub', placePub);
        formData.set('publisher', publisher);
        formData.set('yearPub', yearPub);
       dispatch(newBooks(formData))
    }
    return (
    
     <Fragment>
            <MetaData title={'TUP-T Online Library - Admin'} />
            {/*<div className="row">*/}
                    <SideNavbarAdmin/>
                    <div></div>
                <div className="dashboard-content">
                    <div className="dashboard-page">
                        <div className="dashboard-header">
                            <h1>Add Book</h1>
                        </div> 
                        <div className="dashboard-body">
                            <form className="" onSubmit={submitHandler} encType='multipart/form-data'>
                                <div className="row g-3">
                                    <div className="col md-6">
                                        <div className="form-group">
                                            <label htmlFor="title_field">Title</label>
                                            <input
                                                type="text"
                                                id="title_field"
                                                className="form-control"
                                                name='title'
                                                value={title}
                                                onChange={(e) => setTitle(e.target.value)}
                                            />
                                        </div>
                                        ...
}

This is bookAction.js:

export const newBooks = (bookData) => async (dispatch) => {
    try {

        dispatch({ type: NEW_BOOK_REQUEST })

        const config = {
            headers: {
                 "Content-Type": "multipart/form-data"
                 // "Content-Type": "application/json"
            }
        }
        // for(var pair of bookData.entries()) {
        //    console.log(pair[0]+ ', '+ pair[1]); 
        // }

        const { data } = await axios.post('/api/v1/book/new', bookData, config)

        dispatch({
            type: NEW_BOOK_SUCCESS,
            payload: data
        })

    } catch (error) {
        dispatch({
            type: NEW_BOOK_FAIL,
            payload: error.response.data.message
        })
    }
}

This is bookController.js:

exports.createBook = async (req, res, next) => {
    console.log(req.body);
    }

My app.js on the backend:

const express = require("express");
const app = express();
const cookieParser = require("cookie-parser");
const errorMiddleware = require("./middlewares/errors");
app.use(express.json());
app.use(cookieParser());
app.use(errorMiddleware);
app.use(express.urlencoded({ extended: true }));
const book = require("./routes/book");
const auth = require("./routes/auth");
app.use("/api/v1", book);
app.use("/api/v1", auth);
app.use(errorMiddleware);
module.exports = app;

Solution

FormData send multipart/form-data, hence the parsers you used in app.js won’t work. The common and easy way is to use multer. First install it:

npm install --save multer

Then where you hade urlencoded, and cookieParser use this:

const multer = require('multer');
const upload = multer();
app.use(upload.none())

I’m using this upload.none() because you seem to be not sending any file. If you are, you can visit the above link, mutter will let you do that.

Also you can simplify submitHandler since each form input has a name by getting ride of all those states and simply give the form to FormData() as parameter with e.target:

const submitHandler = (e) => {
  e.preventDefault();
  const formData = new FormData(e.target);
  dispatch(newBooks(formData));
};

Answered By – yousoumar

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published