loading...

Deploying a React app with React-Router and an Express Backend

nburgess profile image Nicolas Rousseau Burgess ・6 min read

In this article we will cover how to create a React application using React-Router for routing and an Express backend. We will then deploy it to Heroku. This tutorial offers a simple way to set up an API that can be quickly updated and tested while creating a React application. It may also offer help to those who are new to React. There are several ways to accomplish this goal but I have covered a very simple method that I am most familiar with. If you have a different method or if I have made any mistakes, feel free to let me know.

The source code for this application can be found here.

Technology Used:

  • Express.JS
  • React.JS
  • React-Router

Create The Express App Backend

In order to begin setting up our app, both node.js and npm need to have been installed.

To start we will need to create a parent directory, which can be named anything you want. Here we will call ours react-express-example.

mkdir react-express-example
cd react-express-example

Initialize the project with npm:

npm init -y

Install the express package:

npm add express

Create a file named index.js and enter the following code, this will serve as a most basic express app.

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

const app = express();

// Serve the static files from the React app
app.use(express.static(path.join(__dirname, 'client/build')));

// An api endpoint that returns a short list of items
app.get('/api/getList', (req,res) => {
    var list = ["item1", "item2", "item3"];
    res.json(list);
    console.log('Sent list of items');
});

// Handles any requests that don't match the ones above
app.get('*', (req,res) =>{
    res.sendFile(path.join(__dirname+'/client/build/index.html'));
});

const port = process.env.PORT || 5000;
app.listen(port);

console.log('App is listening on port ' + port);

We call express() in order to create our express application, denoted by the object app. We then create a method to handle a GET request for /api/getList that will send a json response with a list of items. We will call this from our React app later.

Add a script in package.json so that the app is started once placed on the appropriate server. I normally launch my example projects on Heroku.

{
  "name": "react-express-example",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.16.3"
  }
}

Test Our Express Server

At this point we can test our express app to make sure that everything works so far.

Run the express app with the script created above:

npm start

Open up http://localhost:5000/api/getList and you should see the following:

alt text

Create The React App

If you do not already have Create-React-App installed run the following line of code:

npm install -g create-react-app

The next step is to create the actual React app, which we will keep in the client folder. We will do this by running the following command within our project directory:

create-react-app client

The basic React app is now be visible at http://localhost:3000/ after running npm start from within the client folder. If you decide to name this something other than client, you will have to make changes to the Express file, as it is set to point to client/build.

In order for our React app to proxy API requests to the Express app we have created above, we will need to make a change to client/package.json. This is done by adding the line "proxy": "http://localhost:5000"

client/package.json:

{
  "name": "client",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.4.1",
    "react-dom": "^16.4.1",
    "react-router-dom": "^4.3.1",
    "react-scripts": "1.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "proxy": "http://localhost:5000"
}

Adding React-Router

Here we will add React-Router to our project and create two pages, Home.js and List.js.

If you choose not to use React-Router skip to Calling Our Express App. I have had some trouble setting up a simple implementation in the past so I have included it with this tutorial.

Install the following packages for our React project:

npm install -g react-router-dom

Insert the following code into /client/src/index.js:

import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter } from 'react-router-dom';

import './index.css';
import App from './App/App';

render((
    <BrowserRouter>
        <App/>
    </BrowserRouter>
), document.getElementById('root'));

Insert the following code into /client/src/App.js:

import React, { Component } from 'react';
import { Route, Switch } from 'react-router-dom';
import './App.css';
import Home from './pages/Home';
import List from './pages/List';

class App extends Component {
  render() {
    const App = () => (
      <div>
        <Switch>
          <Route exact path='/' component={Home}/>
          <Route path='/list' component={List}/>
        </Switch>
      </div>
    )
    return (
      <Switch>
        <App/>
      </Switch>
    );
  }
}

export default App;

In this segment of code we have created routes for a home page and a page to display our list. Next we will need to create these pages.

After reading an article by Alexis Mangin I began structuring my React projects similar to how describes. At this point, I recommend reorganizing the project to match the image below.

alt text

Create the file Home.js in src/App/pages and include the following code:

import React, { Component } from 'react';
import { Link } from 'react-router-dom';


class Home extends Component {
  render() {
    return (
    <div className="App">
      <h1>Project Home</h1>
      {/* Link to List.js */}
      <Link to={'./list'}>
        <button variant="raised">
            My List
        </button>
      </Link>
    </div>
    );
  }
}
export default Home;

We have created a button that will link to List.js.

Calling Our Express App

Create the file List.js in src/App/pages and include the following code:

import React, { Component } from 'react';

class List extends Component {
  // Initialize the state
  constructor(props){
    super(props);
    this.state = {
      list: []
    }
  }

  // Fetch the list on first mount
  componentDidMount() {
    this.getList();
  }

  // Retrieves the list of items from the Express app
  getList = () => {
    fetch('/api/getList')
    .then(res => res.json())
    .then(list => this.setState({ list }))
  }

  render() {
    const { list } = this.state;

    return (
      <div className="App">
        <h1>List of Items</h1>
        {/* Check to see if any items are found*/}
        {list.length ? (
          <div>
            {/* Render the list of items */}
            {list.map((item) => {
              return(
                <div>
                  {item}
                </div>
              );
            })}
          </div>
        ) : (
          <div>
            <h2>No List Items Found</h2>
          </div>
        )
      }
      </div>
    );
  }
}

export default List;

Testing Our Final App

At this point the project should be up and running. To test the project run npm start from both the project's home directory and from within the client directory. After selecting My List from the homepage, we should then see the three items from our Express server.

alt text

Deploying To Heroku

Before uploading to Heroku we need to determine how to build our client code. The Express points to client/build, which we do not have before building our React app. With Heroku we can add a heroku-postbuild script so that the React app is built after we push our code, rather than having to upload compiled code.

Edit package.json in the parent directory and add the following script (not /client/package.json):

  "scripts": {
    "start": "node index.js",
    "heroku-postbuild": "cd client && npm install --only=dev && npm install && npm run build"
  }

Heroku will now enter the client directory and create the production build of the React app for us.

If you already have the Heroku tool-belt installed, deploying is as easy as running the following commands:

git init
git add .
git commit -m "Initial commit"
heroku create
git push heroku master

Discussion

markdown guide
 

are you interested to form a team for reactjs freelance projects ? i'm looking for react developers everyway

 

Hi,
how are you doing

i am interested to be a part of experienced developers to enhance my skills
i am a software engineer having 5 years or working experience in mobile web and desktop apps development

looking forward to hearing from you soon

best regards
devasad

 

Hi Ahmed,

I am interested in undertaking React projects. I am fairly new to it, but I am confident I can contribute in a React team. Feel free to contact me if you want to continue our conversation.

 

Hi Ahmed, I am very much interested in React projects. You can contact me anytime

 
 

I am really interested. Is this still up?

 

Hey Ahemd,

Lemme know if this is still open, I'm interested

 

Hi,

First of all thanks for the article, it is a great help.:D
I have done all the things you mentioned and I am facing 2 issues,
1) res.json() in List.js gives an error that '<' token is found i.e. syntax error but I didn't use the method just for sake to deploy on heroku.
2) I deployed on heroku succesfully but when I open my app on heroku I am shown "Not Found" error (most probably 404).

Can you help?

 

Be sure to remove the git folder (generated by Create React App) in the client directory. Doing so will allow Heroku to build your React app and fix the "Not Found" error.

 

i have the same issue with deploying to heroku.

 

Error when typing "npm start" in home directory

thats what it says
npm ERR! code ELIFECYCLE
npm ERR! errno 255
npm ERR! react-express-ex@1.0.0 start: node ../index.js | react-scripts start
npm ERR! Exit status 255
npm ERR!
npm ERR! Failed at the react-express-ex@1.0.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:

any solution to this problem

 

I absolutely love this, thank you :) the only thing i would like to know is, after the app has been deployed to Heroku, is there a way to configure anything so that if the user visits theapp.herokuapp.com/list it will render the list component? at the moment i am getting "Not found", everything else working beautifully

 

Hey, Nicolas thank you for this awesome tutorial, it's was very helpful to understand the connection between both layers.

I create my own project structure using KeystoneJS and react and was great! I'm using in my personal projects right now.

 

Hello Nicolas,

First of all, thanks a lot for your work. Anyway, I have an issue with your project. If you run the server and then:

cd client
npm install
npm run build
http-server build

The App will be deployed in localhost:8080 and works. But, if I access to the list and reload the page:

[Thu Nov 08 2018 11:35:00 GMT+0100 (GMT+01:00)] "GET /list" Error (404): "Not found"

That's because the HTTP static server doesn't found the list file.

I'm pretty sure that if you access directly to: yourHerokuServer/list you will find the same problem.

There is a little workaround: copy the index.html to list and mark its content type as text/html, but I strongly believe that we need another solution to this issue :/

Thanks for your time.

UPDATE: Same question resolved here: stackoverflow.com/questions/279283...

 

What is the purpose of npm install --only=dev in the heroku-postbuild script for building the client? I tried removing that bit and it still worked.

 

At the time of writing, I believe that Heroku installed only the production dependencies by default, ignoring the development dependencies under devDependencies. npm install --only=dev had to be added for the project to build correctly. I think this default behavior has been changed so it may no longer be needed!

 

I'm getting this error when I try to start the react app:

Failed to compile
./src/index.js
Module not found: Can't resolve 'react-router-dom' in 'C:\Users...'

I followed the tutorial. Am I missing something?

 

in client folder do npm install react-router-dom. It wasn't mentioned in the tutorial

 

Hi Nicolas,
Thanks for this article. It was really useful for me to get started with a React/Express project.
I would like to ask you if there is a way to deploy by uploading a build folder manually to a server. I am a React beginner so apologies for any mistake in my question.
Kind regards,
João

 

I have a followed your approach but i am facing a issue.

I have one home page route at express from which i want to pass through a middle-ware function isResturantOpen when ever the user visits the react app.

app.get('/',passportConfig.isResturantOpen, (req,res) =>{
console.log('reached')
res.sendFile(path.join(__dirname+'/client/build/index.html'));
});

but when i ever i tries to visit my website home of react.js at this url
localhost:5000/ then my above route does not work

but all the routes are being going to this main route.

app.get('*', (req,res) =>{
console.log(req)
// console.log(res)
res.sendFile(path.join(__dirname+'/client/build/index.html'));
});

so now if i want to apply some different express.js middle ware functions on each different routes then how can i do this..?

Can you please help me on this.?

 

I have the same problem. The router entry in package.json doesn't seem to be working. the /api calls still go to the react app on port 3000.
Here is my package.json file:
{
"name": "ubwo",
"version": "0.1.0",
"private": true,
"proxy":"localhost:5000/",
"dependencies": {
"react": "16.8.6",
"react-dom": "16.8.6",
"react-redux": "7.1.0",
"react-router": "5.0.1",
"react-router-dom": "5.0.1",
"react-scripts": "3.0.1",
"redux": "4.0.1",
"redux-thunk": "2.3.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"resolutions": {
"browserslist": "4.6.2",
"caniuse-lite": "1.0.30000974"
}
}

Any ideas what I can try to get this working?

 

You could try using http://localhost:5000 instead of what you have now (localhost:5000/). (Actually, having written that I'm not sure that IS the problem...this board seems to be reformatting URLs that aren't backtick-ed.)

Just FYI: I started from zero with this tutorial a few hours ago and got everything 100% working (including a Heroku deploy). So any bugs you might encounter are probably not in the posted code.

 

So I would need to run npm start for both express and the client? Is the proxy you added just for local development? How would I deploy this test code of yours to production

 

You would need to run npm start for both locally. It works in production as well, I'll add a section on deployment tonight.

 

For running locally from your ./client folder, you can modify your package.json script to start both at the same time:

"scripts": {
    "start": "node ../index.js | react-scripts start",
    ...
}

And when you C to terminate batch job, it will terminate both front and back localhosts.

 

Thanks. I would guess you would want to compile the code in the client folder so yeah, would love to read about that process

I've added a short section, I hope that everything is clear

Yes this makes sense. Your creating a Node Heroku setup and it will run that. Thanks

 

Super helpful guide! Straight forward setup and introduced me to some technologies and concepts.

 

I wasted my 4 hours in looking for this way. Thanku for this article

 

Hi Nicolas,

Thanks for the article. It seems that when I make the fetch request for the list at ('api/getList') I get a 404 error. It seems that the request is ignoring the proxy and trying to go to 'localhost:300/api/getList' rather than 'localhost:5000/api/getList'.

If I fully define the path in the fetch request ie fetch('localhost:5000/api/getList') then I get an error:
Access to fetch at 'localhost:5000/api/getList' from origin 'localhost:3000' has been blocked by CORS policy: blah blah blah

I understand that one should never really unblock CORS. So my question is, how to I make get requests to my server? And what is the point in the 'proxy' if it does not solve this problem?

 

try adding cors headers if you're facing the cors related problem

 

The proxy will work in local machine only, in heroku the port might be different isn't it?

 

Is it possible to deploy this on Apache?

 
 

Awesome .best totorial I have ever came onto in devto

 

Great article. Helped me a lot.
Keep up the good work.

 
 

Great article, Nicolas! It really helped me with a project I was hitting the wall with. Cheers.

 

Nicolas this is wonderful explanation. Keeping everything simple! Thank you!

 

The only guide I found that worked right away, thanks broski

 

How can we publish in plesk? We have a shared server with Windows - I think with IIS - and node.js (if necessary)... Can you help us?

 

Rad walkthrough. Thanks! :)

 

While doing this there is a Problem i get when i manually input the routes like \list i get error of cannot GET \list . How to solve this ??.