DEV Community

Cover image for How to deploy React + Node.js application to Heroku
Yogesh Chavan
Yogesh Chavan

Posted on • Updated on

How to deploy React + Node.js application to Heroku

In this article, we will explore, Step by step instructions for deploying React + Node.js app to Heroku which we created in my previous article HERE

Let's get started

Clone the repository code from HERE

To run the application, execute following commands

In first terminal

1. yarn install
2. yarn start
Enter fullscreen mode Exit fullscreen mode

In another terminal

1. cd server
2. yarn install
3. yarn start
Enter fullscreen mode Exit fullscreen mode



Once both the servers are started, you can access the application at http://localhost:3000/

Now, let's start with steps to deploy to Heroku.

Heroku requires, the application to have a start script.

Open server/package.json and check if we have a script with name start in package.json file

"start": "nodemon server.js"
Enter fullscreen mode Exit fullscreen mode

Nodemon is good when we are developing on local environment but on Heroku, we don't need nodemon but just node is required.

So remove nodemon entry from dependencies section and change script from

"start": "nodemon server.js"
Enter fullscreen mode Exit fullscreen mode

to

"start": "node server.js"
Enter fullscreen mode Exit fullscreen mode

Now, your package.json will look like this

{
  "name": "server",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "axios": "0.19.2",
    "cors": "2.8.5",
    "express": "4.17.1"
  }
}
Enter fullscreen mode Exit fullscreen mode

By default, Heroku runs our Node.js application on some randomly available port which is stored in process.env.PORT variable. So we need to make sure, when we start the server we are using that environment variable.

We have already added the code for it in our server.js file

const PORT = process.env.PORT || 5000;
Enter fullscreen mode Exit fullscreen mode

which says take the port value from process.env.PORT environment variable and if the environment variable with name PORT does not exist then use default value of 5000.

Then we are starting our server by accessing the variable PORT

app.listen(PORT, () => {
  console.log(`server started on port ${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

Now, open main package.json and change

"scripts": {
  "start": "react-scripts start",
  "build": "react-scripts build",
  "test": "react-scripts test",
  "eject": "react-scripts eject"
}
Enter fullscreen mode Exit fullscreen mode

to

"scripts": {
  "start-client": "react-scripts start",
  "build": "react-scripts build && (cd server && yarn install)",
  "test": "react-scripts test",
  "eject": "react-scripts eject",
  "start": "cd server && yarn start"
}
Enter fullscreen mode Exit fullscreen mode

Here, we have done following changes

  1. Changed the script name from start to start-client
  2. Changed build command
  3. Added new start script to start the Node.js server

The reason for specifying yarn install in build command is that, Heroku will run this command while building the app so before running start command, node_modules folder will be available

Now, open src/utils/constants.js and change

export const BASE_API_URL = 'http://localhost:5000';
Enter fullscreen mode Exit fullscreen mode

to

export const BASE_API_URL = '';
Enter fullscreen mode Exit fullscreen mode

Now, stop both the servers which we started initially by running yarn start command.

Now, run the yarn build command which will execute the following command

react-scripts build && (cd server && yarn install)
Enter fullscreen mode Exit fullscreen mode

Now, once the command is executed successfully, run the start script

yarn start
Enter fullscreen mode Exit fullscreen mode

which will execute the following command

cd server && yarn start
Enter fullscreen mode Exit fullscreen mode

Now, you can access the application by visiting http://localhost:5000/
and your Node.js API will also be accessible on the same port 5000 at http://localhost:5000/jobs

The reason for React and Node.js application running on the same port is because when we run the yarn build command, it creates a build folder in our project which contains all the code of our React application and in server/server.js file, we have the following code

const buildPath = path.join(__dirname, '..', 'build');
app.use(express.static(buildPath));
Enter fullscreen mode Exit fullscreen mode

This code will take the contents of build folder and If the 'build' folder contains .html file, then Express server will render the contents of that file when we access http://localhost:5000/.

And our Node.js APIs are already available on Port 5000 so both the applications are running on same port and therefore we don't need to execute two separate commands in separate terminals to start the React App and Node.js application.

So now there is no need of using cors npm module to add as a middleware in server.js

app.use(cors());
Enter fullscreen mode Exit fullscreen mode

This was only required because previously our React application was running on port 3000 and Node.js application was running on Port 5000 and browser does not alllow accessing data from different ports because of Cross-origin resource sharing (CORS) policy. So to allow our Node.js server accept requests from any client, we added the cors package.

But its not required now. So remove the cors package from server/package.json file.

Your updated package.json will look like this now

{
  "name": "server",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "axios": "0.19.2",
    "express": "4.17.1"
  }
}
Enter fullscreen mode Exit fullscreen mode

Also remove the import for cors in server.js file and its use.

const cors = require('cors');
app.use(cors());
Enter fullscreen mode Exit fullscreen mode

So your final server.js file will look like this

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

const PORT = process.env.PORT || 5000;

const buildPath = path.join(__dirname, '..', 'build');
app.use(express.static(buildPath));

app.get('/jobs', async (req, res) => {
  try {
    let { description = '', full_time, location = '', page = 1 } = req.query;

    description = description ? encodeURIComponent(description) : '';
    location = location ? encodeURIComponent(location) : '';
    full_time = full_time === 'true' ? '&full_time=true' : '';
    if (page) {
      page = parseInt(page);
      page = isNaN(page) ? '' : `&page=${page}`;
    }
    const query = `https://jobs.github.com/positions.json?description=${description}&location=${location}${full_time}${page}`;
    const result = await axios.get(query);
    res.send(result.data);
  } catch (error) {
    res.status(400).send('Error while getting list of jobs.Try again later.');
  }
});

app.listen(PORT, () => {
  console.log(`server started on port ${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

Now, we're done with the code changes.

Create a new repository in GitHub and let's push the changes to Github

Add all changes to staging area

git add --all .
Enter fullscreen mode Exit fullscreen mode

Commit the changes

git commit -m "Changes for heroku"
Enter fullscreen mode Exit fullscreen mode

Push the changes to github

git push origin master
Enter fullscreen mode Exit fullscreen mode

Now, let's deploy the application to Heroku.

  • Login to your Heroku account and navigate to dashboard HERE

  • Click on the New button from top right section and select Create new app option

New Button

  • Provide the name for the App and click on create app button

Create App

  • Click on Connect to GitHub Link under deployment method section

Connect to GitHub

  • Enter name of your GitHub repository and click on Search button

Select Repository

  • Click on the Connect button to connect the repository to Heroku

Connect Repository

  • Select the branch to deploy (It's master in our case) and click on Deploy Branch button

Deploy Branch

  • Now, your deployment will start

Deployment

  • Once it's completed, You will see a success message along with the option to view your deployed App

Deployment Success


You can find the complete GitHub source code for this article HERE and live demo of the App deployed on Heroku HERE

Note: For Heroku, If you're using a free account, then your application will sleep after 30 minutes of inactivity and once new request comes, the application will start again so it might take some time to load the application.

Don't forget to subscribe to get my weekly newsletter with amazing tips, tricks and articles directly in your inbox here.

Top comments (19)

Collapse
 
sajpanchal profile image
Info Comment hidden by post author - thread only accessible via permalink
Saj Panchal

Hello,
can you please look into my code? I just use the approach that you explained. I am able to create an app on localhost:5000 but when I deployed it in Heroku. I can't get the response from the POST request. All it is giving me is 405 error. I have been fixing it for 2 days but not getting any solutions. If you can help me it will be highly appreciated.

Here is the error code:
Uncaught (in promise) Error: Request failed with status code 405

here is my code:
github.com/sajpanchal/portfolio

Collapse
 
myogeshchavan97 profile image
Yogesh Chavan • Edited

@sajpanchal There are some changes you need to make.

  • add .gitignore file to the server folder with node_modules as content of the file so it will not be pushed to GitHub.
  • add dotenv package inside dependencies section of server/package.json file and remove it from devDependencies.
  • don't push the build folder to Github. You have added a build folder inside the server folder, please remove that. As build folder will be created outside the server folder, change the reference of the build folder from server/index.js file to this
path.join(__dirname, '..', 'build')
Enter fullscreen mode Exit fullscreen mode

instead of

path.join(__dirname, 'build')
Enter fullscreen mode Exit fullscreen mode
  • Remove the following entry from the package.json file
"engines": {
    "node": "12.x"
},
Enter fullscreen mode Exit fullscreen mode
  • remove the un-necessary console.log statements, you're logging password environment variable value to the console in server folder
  • remove the hardcoded email 'abc@gmail.com' and subject from the contact us form
  • make sure to add environment variables from UI on heroku in settings -> config vars and then re-deploy the app by clicking the deploy branch button again.

Here's a deployed version of your portfolio. I have not entered the valid email and password in environment variables so the contact us form will not work. Make sure you add correct values for following environment variables:

USER_PASSWORD=
USER_EMAIL=
RECEIVER_EMAIL=
Enter fullscreen mode Exit fullscreen mode
Collapse
 
letsbsocial1 profile image
Maria Campbell

Thanks so much for this, I never had a front and back using React. Only ejs and stuff so there had never been this kind of issue. Tested it locally and it worked! And now will deploy to remote (Heroku) and see what happens!

Thread Thread
 
letsbsocial1 profile image
Maria Campbell

Yogesh, it worked like a charm! Again, thanks so much!

Thread Thread
 
myogeshchavan97 profile image
Yogesh Chavan

Glad to hear that @letsbsocial1 . Thank you 🙏

Collapse
 
sajpanchal profile image
Saj Panchal • Edited

Thank you. Actually, I fixed it earlier by reinstalling node packages and creating a new Heroku app. It started to work after that updates without doing anything in the code. I appreciate your response. The mistakes you pointed out will be useful in the future though.

Collapse
 
primelos profile image
carlos fabian venegas

Deployed but had some problems... I deleted the heroku app tried again but this time I got a error in the build-> Node version not specified in package.json. So i added this in my package.json and it worked...
"engines": {
"node": "14.15.1",
"npm": "7.6.2"
},

Collapse
 
myogeshchavan97 profile image
Yogesh Chavan

Awesome 👍

Collapse
 
lenicholsdev profile image
L.E. Nichols

I was able to get my separated frontend and backend wired perfectly for heroku following these instructions. Thanks for this.

Collapse
 
myogeshchavan97 profile image
Yogesh Chavan

Glad to hear that @lenicholsdev . Thank you 🙂

Collapse
 
kushalnl7 profile image
Kushal Lahoti

Hi @myogeshchavan97, Thanks for this. I have tried your solution to connect my frontend and backend applications to one, and it worked! But, I am facing a problem for all other endpoints instead of "/". I am not able to fetch the any other page. It's only working for home page. Can you please help me with what mistake I might be committing here?

Collapse
 
kushalnl7 profile image
Kushal Lahoti • Edited

UPDATE: Added the following code in server/index.js to resolve it.

app.get('/*', function(req, res) {
    res.sendFile(path.join(__dirname, '..', 'build', 'index.html'), function(err) {
      if (err) {
        res.status(500).send(err)
      }
    })
  })
Enter fullscreen mode Exit fullscreen mode

Make sure that you add this code after such functions in your index.js file.

app.use("/primary_endpoint1", require("./routers/Router1"));
app.use("/primary_endpoint2", require("./routers/Router2"));
Enter fullscreen mode Exit fullscreen mode
Collapse
 
dygopa profile image
Dylan González

You just save my life with this man, thanks a lot

Collapse
 
myogeshchavan97 profile image
Yogesh Chavan

glad to hear that 🙏

Collapse
 
kanetoomer profile image
kane-toomer

Hello Yogesh,

I have followed all of the steps in both articles but when I click search, loading pops up really fast and disappears w/out displaying the jobs. Any suggestions?

Collapse
 
myogeshchavan97 profile image
Yogesh Chavan • Edited

@kanetoomer The GitHub Jobs API is deprecated and is no longer available. You can try using the hardcoded JSON data from this file and use it so you will see the list of jobs displayed when clicked on search.

Collapse
 
kanetoomer profile image
kane-toomer

Thank you, I have figured it out now.

Collapse
 
samx23 profile image
Sami Kalammallah

Will it work if I have .env and add vars manually ? the environtment using REACT_APP_

Collapse
 
myogeshchavan97 profile image
Yogesh Chavan

we don't push .env file to github for security reasons. So you will need to add environment variables through UI for Heroku.

Some comments have been hidden by the post's author - find out more