4

I'm developing a simple app using Express.js and React, both applications in the development environment (local machine) are on different ports, the server (Express) on localhost: 3001 and the Frontend (React) on localhost: 3000.

The problem I have is that trying to send data from the Frontend using Fetch API Express does not receive the data sent via POST by React. I did a test doing a POST from a normal form and the server did accept the parameters I send via POST, so I think the problem is something with the call from Javascript using Fetch API.

In Express I already installed the cors module to accept requests that are not of the same domain but the problem persists.

Below is a snippet of code from both for a better understanding.

...
// handler recieves the 'e' event object
formPreventDefault(e) {
    var headers = new Headers();
    headers.append('Accept', 'application/json, application/xml, text/plain, text/html, *.*');
    headers.append('Content-Type', 'multipart/form-data; charset=utf-8');
    headers.append('Access-Control-Allow-Origin', '*');
    headers.append('Access-Control-Allow-Headers', 'Content-Type');

    var myForm = document.getElementById('myForm');
    var formData = new FormData(myForm);
    formData.append('email', 'demo@domain.com');
    formData.append('password', '12345');

        var options = {
        headers: headers,
        mode: 'no-cors', // crossdomain *
        method: 'post',
        body:  JSON.stringify({ 'email': 'admin@domain.com', 'password': '12345' }) // formData
    };

    var request = new Request('http://localhost:3001/user/signin', options);

    fetch(request).then(function(response) {
        console.log("Success");
        return response;
    }).then(function(json) {
        console.log(json);
        }).catch(function(err) {
        console.log("Error " + err);
    })

    // prevent submit form
    e.preventDefault();

  }

  render() {
    return (
      <div className="row">
        <div className="col-md-4  ">
          <form id="myForm" action="http://localhost:3001/user/signin" method="post" onSubmit={this.formPreventDefault}>
                <div className="form-group">
                  <label htmlFor ="email">Email address</label>
                  <input type="email" className="form-control" id="email" name="email" placeholder="Email" />
                </div>
                <div className="form-group">
                  <label htmlFor ="password">Password</label>
                  <input type="password" className="form-control" id="password" name="password" placeholder="*******" />
                </div>
                <button type="submit" className="btn btn-default">Submit</button>
              </form>
        </div>
      </div>
    );
  }
}
...

Express Controller

...
// Handle User Sign In on POST
exports.user_create_post = function(req, res) {
    console.log(req.body); // This always returns an empty value
    //Check that the name field is not empty
    req.checkBody('email', 'Email address is required').notEmpty();
    req.checkBody('password', 'Password is required').notEmpty();

    //Run the validators
    var errors = req.validationErrors();

    //Create a genre object with escaped and trimmed data.
    var user = new User(
      { email: req.body.email, password: req.body.password }
    );

    if (errors) {
        //If there are errors render the form again, passing the previously entered values and errors
        // res.render('users/sign_up', { title: 'Sign Up', user: user, errors: errors });
        res.json({ user: user, errors: errors });
        return;
    }
    else {
        // Data from form is valid.
        //Check if User with same email already exists if not the new user is saved
        User.findOne({ 'email': req.body.email })
            .exec( function(err, found_user) {
                 if (err) { return next(err); }

                 if (found_user) {
                     //User exists, redirect to the home page
                     // res.render('users/home', { title: 'Home', user: found_user });
                     res.json({ user: found_user });
                 }
                 else {
                    // Save user
                     user.save(function (err) {
                       if (err) { return next(err); }

                       // Create session
                       req.session.user_id = user._id.toString();

                       // User saved. Display user profile page
                       // res.render('users/home', { title: 'Home', user: user });
                       res.json({ user: user });
                     });

                 }

             });
    }
};
...

Any help is welcome

Wilcho
  • 123
  • 1
  • 6
  • Since both express and react are not of the same origin, for the fetch request, you should ensure `{ mode: 'cors' }` is set for the fetch options. It may be useful to verify if cors is working or if there is some HTTP error code returned in the resposne by logging the 'response' object or the response.status value in the first .then() following fetch(). Also, it may help to verify the API HTTP request/response by checking the network tab of the developer console on your browser to see if there is any problem with the request. – Finbarr O'B Jul 25 '17 at 13:58
  • I 've "mode: 'no-cors'" in the option of the fetch. This is that the Response Object returns from the Resquest to the Express server: Response { type: "opaque", url: "", redirected: false, status: 0, ok: false, statusText: "", headers: Headers, bodyUsed: false } – Wilcho Jul 25 '17 at 16:58

3 Answers3

0

If your production environment will host both the frontend and server, your requests eventually won't have to deal with CORS. In this case, I would suggest targeting Port 3000 in your frontend react Fetch request, and using the "proxy" feature Proxying API Requests in Development on your Frontend server for development.

Basically, put this in your package.json

"proxy": "http://localhost:3001",

and change your request to:

var request = new Request('http://localhost:3000/user/signin', options);

EDIT Cleanup for fetch request required for this to work:

fetch('http://localhost:3000/user/signin', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
    },
    body: JSON.stringify({ 'email': 'admin@domain.com', 'password': '12345' }) // formData
}).then(function(response) { 
...
nAv
  • 129
  • 6
  • You'll need to be more specific. What didn't work? The request never reached your backend server? The request reached but the contents are empty? – nAv Jul 25 '17 at 17:11
  • If it is reaching your server but you can't read the contents, instead of using 'multipart/form-data; charset=utf-8' for your Content-Type try setting your header to 'Content-Type': 'application/json' – nAv Jul 25 '17 at 17:13
  • I added "proxy": "http://localhost:3001" to my package JSON and changed the Request URL to 'http://localhost:3000/user/signin' and doesn't worked. The resquest reached my backend server and sending the POST data with message 200(Ok), the server receive the resquest(I can see that in console) but when I try to get the "req.body" is empty on the server – Wilcho Jul 25 '17 at 17:34
  • 1
    Try the Content-Type change I mentioned above, if that doesn't work perhaps you need body-parser: https://stackoverflow.com/questions/5710358/how-to-retrieve-post-query-parameters – nAv Jul 25 '17 at 17:38
  • 1
    I've tried with the Content-Type that you mentioned above and my Express server has the body-parser configured. App repo https://github.com/wilcho-vnz/jarvis – Wilcho Jul 25 '17 at 17:45
  • 1
    Remove 'Accept' header. Remove the mode: 'no-cors' line. Make your request to localhost:3000 (not 3001). You should be good to go – nAv Jul 25 '17 at 18:26
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/150138/discussion-between-nav-and-wilcho). – nAv Jul 26 '17 at 05:41
  • It worked after the insertion of "Content-Type" header. Seems that proxy ignores body of POST requests unless you explicitly sets Content-Type. – João Bruno Abou Hatem de Liz Aug 06 '18 at 01:41
  • Im using 'Content-Type': 'application/json' but my requests body is still coming back empty – 0xD1x0n Oct 26 '21 at 20:01
0

I've figured it out how to solve the problem, here's the code with the help of this post https://github.com/matthew-andrews/isomorphic-fetch/issues/34.

I hope that helps somebody

Thanks for help me

...
// handler recieves the `e` event object
  formPreventDefault(e) {

        var headers = new Headers();
        headers.append('Content-Type', 'application/json');

        var email = document.getElementById('email').value;
        var password = document.getElementById('password').value;

        if(email !== '' && password !== '') {

            var options = {
                method: 'POST',
            headers: headers,
            mode: 'cors',
            cache: 'default',
                body:  JSON.stringify({ 'email': email, 'password': password }) // formData
            };

            var request = new Request('http://localhost:3001/user/signin', options);

            fetch(request).then(function(response) {

            console.log("Success");
                return response;

            }).then(function(json) {

            console.log(json.errors);

            }).catch(function(err) {

            console.log("Error " + err);

            })

        }

        // prevent submit form
      e.preventDefault();

  }
...
Wilcho
  • 123
  • 1
  • 6
0

Your primary problem is that you aren't setting your request body to be your form data. Instead you are manually encoding your form data as json. Big tipoff: you create the formData variable but never use it anywhere.

Try this:

options = {
    headers: headers,
    mode: 'no-cors', // crossdomain *
    method: 'post',
    body:  formData
};
Brandon
  • 38,310
  • 8
  • 82
  • 87