/ React

How to create a React app with a local Express API

The nice people at Facebook have created a tool for bootstrapping your React app called create-react-app, it creates all the boiler plate code you need to quickly get the basics of your app up and running.

By default this will only support the development of a front end client i.e. the code will only run inside the browser. However, what if wanted to create and use backend a API?

One way achieve this would be to have an external server and set the CORS headers, allowing your app to access the API cross domain. For example your app could sit at app.example.com which then uses a REST API endpoint situated at api.example.com.

But what if you wanted your REST API endpoint to reside on the same domain as the app e.g app.example.com/api?

In this tutorial I'll show you how to achieve the above. While we will be using Express to create the API, you can infact use any http backend technology e.g. Python, Java or Ruby etc.

First lets create a new express project:

mkdir app-with-api
cd app-with-api
npm init
npm install express --save

Next create a file (in your 'app-with-api' directory) called 'api.js', and populate it with the following code:

var express = require('express');

let server = express();

server.get('/api/users', (request, response) => {

    //dummy 'users' data
    response.status(200).json([
        {
            name: "Mr Foo",
            email: "foo@example.com"
        },
        {
            name: "Mrs Bar",
            email: "bar@example.com"
        },
        {
            name: "Ms Baz",
            email: "baz@example.com"
        }
    ]);
});

server.listen(3001);

You can node start the api server by running:

node api.js

you can now verify that this api server is working correctly by going (in your browser) to http://localhost:3001/api/users, where you should to the dummy data (specified in 'api.js') as JSON.

We'll now create the main app. Open up a new terminal and run the following inside 'app-with-api':

create-react-app app
cd app
npm start

you should now have a React app development server running. To verify everything is working OK go to http://localhost:3000 where you should see a default React app page.

Ok, we've created the frontend and backend services lets now intergrate them. To do this we'll use the "proxy" feature that comes with the "create-react-app" boilerplate.

Open up "app-with-api/app/package.json" and add in the following "proxy" attribute:

{
    ...
    "proxy": "http://localhost:3001",
    ...
}

now restart the React development server (hit ctrl+c and re-run npm start).

Your "users" api endpoint will now be available at http://localhost:3000/api/users. However, here is a gotcha - if you try to access it directly through the browser you'll get the index.html page, this is because there is some content negotaion involved. The React development will only delegate requests to the proxy server if:

  1. There is no static asset available
  2. The "Accept" http request header has not been set with "text/html"

In order to demonstrate this content negotion logic we can use the "curl" command line tool:

# this will get JSON (via proxy) as no "Accept" header has been set with "text/html"
curl http://localhost:3000/api/users

# this will get "index.html" as the "Accept" header has been set with "text/html"
curl --header "Accept: text/html" http://localhost:3000/api/users

Finally we can now alter our React app to make a request to the API and display the dummy users. Open up "app-with-api/app/src/index.js" and replace it with the following:

import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component {

    state = { users: [] }
    
    componentWillMount(){
        fetch('/api/users').then(response => {
           response.json().then(users => this.setState({users: users}));
        });
    }
    
    render(){
        return (
            <table>
                <thead>
                    <tr>
                        <td>Name</td>
                        <td>Email</td>
                    </tr>
                </thead>
                <tbody>
                    {
                        this.state.users.map(user => {
                            return (
                                <tr>
                                    <td>{user.name}</td>
                                    <td>{user.email}</td>
                                </tr>
                            );
                        })
                    }
                </tbody>
            </table>
        );
    }
    
}

ReactDOM.render(<App />, document.querySelector('#root'));

That's it! If you got to http://locahost:3000 you'll see a table displaying the users.

Note: you can optionally remove redundant files by running the following:

rm app/src/App.* app/src/index.css app/src/logo.svg