8

I'm working on a project with the MERN (MongoDB, Express, React, Node) stack and I'm having issues when posting data from a form within a React component to an API endpoint defined in Node.js. When I submit the form the browser just shows a CANNOT POST error. I'm pretty confident that if I create an event handler for the form submit within React and handle the POST using a library such as Axios that I could get around this issue.

But ultimately I believe this problem is because the Node backend is running on a different port to the React front end. Is there a way that I can configure my stack so I can use a standard form POST and potentially have the FE and BE running on the same port?

James Howell
  • 1,392
  • 5
  • 24
  • 42
  • Isn't there any other error detail? You can't run two different apps(servers) on the same port. What do you use for React dev server? – devserkan May 29 '18 at 19:27
  • What is serving your React front end if not the same express app? If you are indeed serving it using another webserver/app on purpose, then you would have to set up a redirect from that server to your express server. – lenkan May 29 '18 at 19:28
  • I'm using an un-ejected create-react-app for the front-end and express for the back-end – James Howell May 29 '18 at 19:31
  • So, do not think to run two servers on the same port (which is not possible), use proxies to make API requests from React side. – devserkan May 29 '18 at 19:33
  • https://medium.freecodecamp.org/how-to-make-create-react-app-work-with-a-node-backend-api-7c5c48acb1b0 @JamesHowell – lenkan May 29 '18 at 20:01
  • How do I do that? Or is it easier to get Express to serve my React app? – James Howell May 29 '18 at 20:01
  • At the end, in production you will serve your React app (after building it) with Express. Here (in CRA) you are serving your frontend for development purposes. With an easy setup you can run both servers at the same time and use a proxy to redirect your API calls. See the medium article @lenkan gave. – devserkan May 29 '18 at 20:12

5 Answers5

13

I see that you are running an un-ejected CRA. That means that when you run npm run start from your create-react-app folder you should have react running on port 3000, the default port.

First I would recommend keeping your server and client code into a separate folder with separate package.json files

Now let suppose you have this code in /server/index.js Its straight out of the express example but the route starts with /api and also will run on port 5000. This is very important and you will see why in a minute.

const express = require('express');
const app = express();

app.get('/api/hello', (req, res) => res.send('Hello World!'))

app.listen(5000, () => console.log('Example app listening on port 5000!'))

Now back into your /client folder where I will assume your CRA is, open package.json and add the following lines:

"proxy": {
  "/api/*": {
    "target": "http://localhost:5000"
  }
},

Try now to do a call the server from react with axios for example:

const helloFromApi = 
  axios
    .get('/api/hello')
    .then(res => res.data);

Hope it helps

UPDATE 10/21/2019

proxy field in package.json must be a string

"proxy": "http://localhost:5000"
bpGusar
  • 133
  • 1
  • 14
ovidb
  • 1,760
  • 13
  • 18
  • i am just wondering, Is it convenient way to do? and what if i have a same route on client as /api/hello? – manan5439 Jul 23 '20 at 16:01
  • @manan5439 It is the recommended way of doing it. Since you are in control of the routes, it should be fairly easy to make sure you respect the convention and avoid having the same route on both client and server. – ovidb Jul 24 '20 at 18:11
  • Does this make it so your api does not need any nginx configuration? (assuming the react app has adequate configuration) – Daniel Sep 05 '20 at 22:46
  • 5000 is the standard http port, so this works great if you only expect API traffic on that port and are directing traffic to the React app via another port. Most production setups seem to forward ports 5000 and 443 to whatever port the React app listens on, so probably better to use a different port number (3001 for example) for API traffic. Setup is the same as above. – user3396385 Nov 16 '21 at 21:10
5

For developing add the following line to the package.json file

"proxy": "http://localhost:{your API port}/"

For production you can setup proxying in app (Express, Nginx, ...) which will serve your static files (React app, styles, etc). Usually using "/api/" mask for determination API request.

4

I know this is late to answer but could be helpful for anyone looking for one more solution. This solution could be applied for a react application or a angular application with a node backend at same port and if you are creating your image with the help of Docker.

So whenever you are deploying your project at production level. Just build your angular or react project with the help of npm run build and in your express app just serve the whole build folder with the help of express static.

So your Docker file could be something like this

# The builder from node image
FROM node:8-alpine as web-app

# Move our files into directory name "app"
WORKDIR /app
COPY package.json /app/
RUN cd /app && npm install
COPY . /app
RUN cd /app && npm run build  // build your front end

EXPOSE 5000

CMD [ "node", "server.js" ] // start your backend

This will start the backend on port 5000.

Now in app.js file or wherever you have your server file in express of yours, serve the build folder like this

app.use(express.static(path.join(__dirname, 'build')))

If you want to test it in your local. You can create above docker file and change the app.js as shown above to serve static files. And then build and start the docker image like this

docker build . -t web-app
docker run -p 5000:5000 web-app

Now your front end gets build at production level and served from express.

Remember in local you can always start both ports for development and use the feature provided by react or angular like auto reloading after changes in your front end and make development easy.

0

But ultimately I believe this problem is because the Node backend is running on a different port to the React front end.

Okay,

MERN is fantastic. My only problem was I couldn't use Mongoose on the React side, I came across this issue and after a few hours, I found a better solution,

No need to put anything on package.json, no need to worry about CORS,

here's a working example of a user registration using mongoose (mongoose will never run on client side, don't waste time, the library modification is time consuming),

start your express server on a port, lets say 3030, and React runs on 3000,

on React side,

constructor(){ 
...
  this.server = server || 'https://my.ip.add.ress:3030'
...
}

register(username, password, signup = true) { 
return this.fetch(`${this.server}/server/register`, {
            method: 'POST',
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                username,
                password,
                signup
            })
        }).then(res => { console.log(res);
            this.setToken(res.token) // Setting the token in localStorage
            return Promise.resolve(res);
        })
}

On Node.JS server (express) side,

Create a folder 'server' and create a file server.js,

var MongoNode   = require('mongoosenode')   // I created this package for just to test mongoose which doesn't run on React side, 
var cors        = require('cors');  //use cors for cross-site request

var options = {
        key     : fs.readFileSync('server.key'),
        cert    : fs.readFileSync('server.cert'),
    };

    /*
     * Cors Options
     */ 
    var whitelist = config.allowedOrigins //put https://my.ip.add.ress:3000 in the allowedOrigins array in your config file
    var corsOptions = {
        origin: function (origin, callback) {
            if (whitelist.indexOf(origin) !== -1) {
                callback(null, true)
            } else {
                callback(new Error('Not allowed by CORS'))
            }
        }
    }
    //specify the port
    var https_port = config.server.port || 3030;

    //use app or any route included to server.js
     app.post('/register', cors(corsOptions),function(req, res) {
        //Process requests
        console.log(req.body);  //see if request payload popping up
        var mn = new MongoNode('mongodb://username:password@127.0.0.1:27017/databasename')

        var user = mn.retrieveModel('User','User').then(async(res) => { 
           try { 
             user = res.model;
              console.log(user);
              user.username = req.body.username
              user.password = req.body.password
              user.token = token_str  //jwt web token to save browser cookie 
              user.save(function(err) {
                 if (err) throw err;
                 console.log('user saved successfully');
                 res.json({ success: true, token: user.token});
              });                  

           }catch(e) { 
             console.log(e);
           }
        })

        user.save(function(err) {
            if (err) throw err;
            //console.log('user saved successfully');
            res.json({ success: true , message: 'user saved successfully', token : user.token });
        });

    }

Voila! it's done easily after a few hours of reading.

Arun Panneerselvam
  • 2,263
  • 1
  • 17
  • 24
0

This is probably what you want:

https://mty-tidjani.medium.com/deploy-nodejs-react-app-on-a-single-port-domain-54a40f1abe16

To summarize, you can set up express to serve the React app as static content from a subdirectory of the server tree. This is the only way I've been able to find to get all content served over a single port, but there may be others. Likely the concept is the same since, as others have mentioned, you can't have two services sharing the same port.

Dharman
  • 30,962
  • 25
  • 85
  • 135
user3396385
  • 186
  • 1
  • 11