How to make an API call in React App

Ragunath Rajasekaran
6 min readOct 23, 2019

--

This tutorial explains about how to consume web services in React application and populate the same in React component.

This is a continuation of my previous one which explains about how to kick start a new React project using ReactBoilerPlate.

What’s in this tutorial?

  • Consume/Fetch/Call web services using Fetch API
  • Populate response in React component

Web Services to consume

You can use any service (URL) to learn this. I am going to demonstrate it by consuming Accounts services (Written in SpringBoot).

Getting Started with SpringBoot: Scaffolding & Annotations (Part2)

Spring Boot Deep Dive — RestController (Part3)

Getting started with Spring Data JPA, Hibernate ORM & Repository (Part 4)

Make Web Services Ready to Consume in Local

SpringBoot project can be found in GitHub.

Checkout data-jpa-end branch

git clone https://github.com/ragunathrajasekaran/em-api.git
cd em-api
git checkout data-jpa-end

Run MySql

Run MySql local instance as mentioned in blog or follow the below steps.

docker-compose up

If you see the below issue then start the docker container using its id

docker ps -a
docker start 0ee6ddeba0ca

Run SpringBoot Project

gradle build
gradle bootrun

PostMan Collection Import

I have committed the postman collection in github as em-apicollection.postman_collection.json. Please import the same if you would prefer to use Postman.

Accounts web services are ready to consume in localhost . Let’s verify by hitting health endpoint.

Reset MySql (If you want to)

If you want to reset accounts table, use the following queries to delete all existing rows and insert the new one.

delete from accounts;

INSERT INTO accounts (id, description, title) VALUES (1, 'My Savings Account', 'Savings');
INSERT INTO accounts (id, description, title) VALUES (2, 'My Credit Account', 'Credit');
INSERT INTO accounts (id, description, title) VALUES (3, 'My Debit Account', 'Debit');
INSERT INTO accounts (id, description, title) VALUES (4, 'My Wish List', 'WishList');

Recap from previous posts

In Previous Post, we have developed the following components(HomeContainer, AccountList & AccountListItem)

Checkout code from Github to begin (data-fetch-begin branch)

git clone -b data-fetch-begin https://github.com/ragunathrajasekaran/em-ui.git

HomeContainer

  • Top level container
  • It has a local state
  • It has a hard coded accounts data (We are going to refactor and fetch accounts through web services)

AccountList

  • Receives accounts from HomeContainer as props
  • Iterate accounts array and render AccountListItem for each Account

AccountListItem

  • Receives an account information from AccountList as props
  • Present it in UI using material ui

Local State(HomeContainer)

We have accounts list and it is hard coded in HomeContainer. We are passing accounts constant to AccountList functional component. Instead, let us assign accounts constant value as an initial state value to HomeContainer. But why? — After we have received response from web service, just by updating the local state would render accounts in AccountList functional component.

const accounts = [
{ id: 1, title: 'Credit Account', desc: 'Credit Card Account', total: 3333 },
{ id: 2, title: 'My Wallet', desc: 'My Personal Waller', total: 30 },
{ id: 3, title: 'Bank Account', desc: 'My Savings Account', total: 10000 },
];

/* eslint-disable react/prefer-stateless-function */
export class
HomeContainer extends React.Component {
constructor(props) {
super(props);
this.state = { accounts: [...accounts] };
}

render() {
return (
<div>
<AccountList accounts={this.state.accounts} />
</div>
);
}
}

Whenever there is a change in HomeContainer’s local state, AccountList will get the latest accounts and show in UI.

API Details

  • URL : http://localhost:8080/accounts
  • HTTP Request Method : GET
  • Http Response :
  • content : contains array of accounts
  • pageable: contains pagination information and we are going to use this in this post
  • other params : we wont use them in this post

Fetch API

fetch() is a method from window object(In global scope) and we can access fetch() method without import. fetch returns promise. Please check MDN documentation for detailed usage.

Let’s Fetch Accounts List

Let us write an arrow function, fetchAccounts, to fetch accounts.

fetchAccounts = () => {

};

fetch returns promise.

fetchAccounts = () => {
fetch('http://localhost:8080/accounts')
};

let’s parse.

fetchAccounts = () => {
fetch('http://localhost:8080/accounts')
.then(response => response.json());
};

Let’s handle success response. On success, we are updating local state with content (Which is array of accounts).

fetchAccounts = () => {
fetch('http://localhost:8080/accounts')
.then(response => response.json())
.then(response => this.handleSuccessResponse(response))
};
handleSuccessResponse = response => {
this.setState({ accounts: [...response.content] });
};

Let’s also handle failure cases. We are console logging the error here.

fetchAccounts = () => {
fetch('http://localhost:8080/accounts')
.then(response => response.json())
.then(response => this.handleSuccessResponse(response))
.catch(error => this.handleErrorResponse(error));
};
handleErrorResponse = error => {
console.log('error :', error);
};

Where shall we invoke fetchAccounts method

componentDidMount method gets called when component is being mounted. We can start our fetch process from it.

componentWillMount() {
this.fetchAccounts();
}

Response’s Account object returns description instead of desc and also it doesn’t return total param. Let us update AccountListItem to show description as secondary information.

const AccountListItem = account => (
<ListItem>
<Avatar>
<ImageIcon />
</Avatar>
<ListItemText primary={account.title} secondary={account.description} />
</ListItem>
);

Let us run and see the output.

What would happen If the service is down?

HomeContainer local state is being assigned with accounts array constant. Let’s remove it and assign an empty array as an initial state for accounts.

constructor(props) {
super(props);
this.state = { accounts: [] };
}

Let’s stop service and run the app.

Local state of accounts array is empty now. As per our logic, we are iterating the accounts array in AccountList and rendering an AccountListItem for each account. An empty accounts array would not render any AccountListItem.

Console log will print the error message.

What happened So far?

  • componentDidMount gets called when HomeContainer is about to mount and it calls fetchAccounts method
  • fetchAccounts method uses fetch to retrieve accounts from server
  • Response Parsing
  • handleSuccessResponse extracts content from Response and assign it to HomeContainer’s local state.
  • AccountList updates in UI

Checkout the final code from Github(data-fetch-end branch)

git clone -b data-fetch-end https://github.com/ragunathrajasekaran/em-ui.git

--

--

Ragunath Rajasekaran

Sr. Tech Lead | Spring Boot | Big Data | AWS | Terraform | Spark | React | Docker