Lab 8 - Making REST API calls
Intro
In this lab you will be seeing how we can communicate with a server using REST.
Pre-requisites
You should have a running "payments ui" application.
1. Get some data from a back end server
-
Ensure you can access the payments application server by visiting: https://payments.neueda.com/swagger-ui/.
-
Create an additional function in the DataFunctions.js module called
getAllPaymentsRestVersion. -
Call this function from the transactions component. For now just call this somewhere near the top of the component (don't try and integrate it yet).
-
In the getAllPaymentsRestVersion function, use
fetchto issue a GET request to the payments server's get-all-transactions REST end-point. (https://payments.neueda.com/api/payment).Note that you need to provide the following header:
accept: application/json. -
Use the
thenmethod to get the response and print it out to the console. -
Run the application and look at the response object in the browser's console. Notice you can't see the data at this point.
End of section code
Click to view sample solution.
export const getAllPaymentsRestVersion = () => {
const responsePromise = fetch("https://payments.neueda.com/api/payment", {
method: "GET",
headers: new Headers({'Accept': 'application/json'})
});
responsePromise.then(response => console.log(response));
}
2. Extract the data returned by the server
-
Within the
thenfunction that has the response object, call thejson()function of the response object. This also returns a promise. -
When the jsonPromise completes, print out the data received to the console.
Hint: you will be creating nested code to make this work!
End of section code
Click to view sample solution.
export const getAllPaymentsRestVersion = () => {
const responsePromise = fetch("https://payments.neueda.com/api/payment", {
method: "GET",
headers: new Headers({'Accept': 'application/json'})
});
responsePromise.then(response => {
response.json().then (data => {
console.log(data);
})
});
}
3. Try to create a good user experience while fetching data
WARNING: This part of the lab will not work... there is a problem which we'll fix in the next section!
- Adjust the
getAllPaymentsRestVersionfunction so that it returns the response promise.
In the transactions component:
-
Set up a stateful variable called loading, with an initial value of
true. -
Change the return method of the component so that the table is only displayed if the loading variable has the value
false. Show a friendly message if it's true. (Hint: use conditional rendering) -
Change the payments array to be a stateful object with an initial value of an empty array.
-
We have a call to the getAllPayments function - this now returns a promise, so store this as a variable.
-
When the promise completes, call its
json()function. -
When the json function completes, extract the data and use it to set the values of the stateful variable payments. Also at this point set the loading variable to
false.
End of section code
Click to view sample solution.
DataFunctions.js
export const getAllPaymentsFetchVersion = () => {
const transactionsPromise = fetch("https://payments.neueda.com/api/payment", {
method: "GET",
headers: new Headers({'Accept': 'application/json'})
});
return transactionsPromise;
}
Transactions.js
const Transactions = () => {
const [payments, setPayments] = useState([]);
const [loading, setLoading] = useState(true);
getAllPayments()
.then(response => {
response.json()
.then(data => {
setPayments(data);
setLoading(false);
})
}
);
return (
<div>
{loading && <p className="loadingMessage">The data is loading please wait...</p>}
{!loading &&
<div>
<table className="transactionsTable">
<thead>
<tr>
<th>Id</th>
<th>Date</th>
<th>Country</th>
<th>Currency</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
{payments.map(payment => TransactionTableRow(payment))}
</tbody>
</table>
</div>
}
</div>
);
}
4. Use useEffect to control when code is run
-
Move the code that will load the data and set the value of payments + loading into a separate function called
loadData. -
Create a useEffect hook that will call this method. The second parameter should be an empty array so that the method is only called when the component is evaluated the first time, and not on subsequent re-evaluations.
-
Place a
console.logline at the start of the transactions component and in the fetching data method, so that you can see how many times each runs. -
Check that this is now working as expected in the browser!
End of section code
Click to view sample solution.
const Transactions = () => {
console.log("transactions is rendering")
const [payments, setPayments] = useState([]);
const [loading, setLoading] = useState(true);
const loadData = () => {
getAllPayments()
.then(response => {
response.json()
.then(data => {
setPayments(data);
setLoading(false);
})
}
);
}
useEffect(() => loadData(), []);
return (
<div>
{loading && <p className="loadingMessage">The data is loading please wait...</p>}
{!loading &&
<div>
<table className="transactionsTable">
<thead>
<tr>
<th>Id</th>
<th>Date</th>
<th>Country</th>
<th>Currency</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
{payments.map(payment => TransactionTableRow(payment))}
</tbody>
</table>
</div>
}
</div>
);
}
5. Upgrade the code to use axios, and implement error handling
-
Stop the application from running
-
Install axios by running the following command in the terminal:
npm install axios
-
Restart the application
-
Within DataFunctions.js, convert the fetch function to an axios function. You'll need to import axios into this file.
-
Adjust the code in Transactions.js to** use the Axios version** (note that there is no longer a separate promise - you can just access resopnse.data)
-
Enhance the code by adding error handling. Use console.log to output if something has gone wrong.
End of section code
Click to view sample solution.
DataFunctions.js
export const getAllPayments = () => {
const transactionsPromise = axios({url : "https://payments.neueda.com/api/payment", method: "GET", headers : {'Accept': 'application/json'} });
return transactionsPromise;
}
Transactions.js
const Transactions = () => {
console.log("transactions is rendering")
const [payments, setPayments] = useState([]);
const [loading, setLoading] = useState(true);
const loadData = () => {
getAllPayments()
.then(response => {
setPayments(response.data);
setLoading(false);
})
.catch(error => {
console.log("something went wrong", error);
});;
}
useEffect(() => loadData(), []);
return ( ... );
}