Chapter 3 - Interacting with the API in our Front End
3.1 Fetching Data from the API with JavaScript
Now that we understand what a REST API is and how we can query data from it we need to find out how we can fetch data from it with JavaScript. In JavaScript there is a native library called Fetch that can be used to query data from REST APIs. Here is some sample code how you can use Fetch:
fetch('http://localhost:5000/tasks') // first you declare the URL that should be queries
.then((response) => response.json()) // then you tell Fetch that it should interpret the response as JSON code
.then((data) => console.log(data)); // Now you can do something with the data, for example printing it to the console.
Nice, we have loaded data from our own api. But the code still looks a bit messy. We first should create a new directory called src/services and create a new file named api.js in it. In this file we write the logic to query the API. For better readability you should put the API calls into functions. Your code for fetching data from the API could look like this:
URL = 'http://localhost:5000';
/**
* Fetch tasks from the API
*
* @return List of tasks
*/
function getTasks() {
fetch(`${URL}/tasks`)
.then((response) => response.json())
.then((data) => return data);
}
This looks so much better. You should not forget to add meaningful comments to your code. Every function should have a Docstring that explains what the function is about, which parameters it accepts and what it returns. Have a look at this plugin for VSCode that helps you to generate docstrings for your code. The commentation of your code will also contribute to the evaluation of your project.
Now it is your turn to implement more methods for querying the API. You can read more about Fetch here. The file api.js should at least contain the following methods:
3.2 Using the methods in your React project to fetch and render a list of tasks
Now that we have written the code for interacting with the REST API the next step is to include these methods in our React project, fetch the data and show the tasks in our app.
Import the functions in your Reacct component's code:
Now we need to declare a state variable where we can store the list of fetched tasks in.
This snippet shows how you can load the tasks from the API. The if-statement checks if the tasks have been loaded akready by checking the value of the 'loading' variable, which is also stored in the component's state. If this is not the case it calls a function named loadTasks which is defibed above that if-statement.
Now that we have fetched the tasks we can display them. For that you have to edit the return statement of the component. This could look like this:
This code above will only render a basic HTML list that looks very rudimenrary. It is your term to add CSS-classes from Bootstrap or your own CSS-Code to the component so that it looks better. If you want to use bootstrap classes for styling the code have a look at the list groups of Bootstrap.
3.3 Add functionality to delete tasks
Now that we are able to render a list of tasks we can add more functionality to our code. Let's first add some code to our component that we can call to delete a task.
This function first sends a DELETE request to the API using the deleteTaskfunction declared in the file api.js. Then it reloads the tasks, because the list of tasks now contains ine element less than before. After that it calls window.alert() to give the user feedback that the task was deleted successfully.
Now we need to add a button to each item of our list of tasks. If this button is clicked the method deleteTask should be called.
Great, we now can read tasks from the API, render them in our React app and we are also able to delete them. I think you now have a basic understanding of how frontend apps interact with REST APIs.
3.4 Implementing a form for creating new tasks
Now it's time to impment the interesting part of our app: creating new tasks. For that we first need to define the logic for creating new tasks in our tasks component. Let's have a look how we can do this.
The function showed above works similar to the deleteTask function. First we send a request to the API, then we reload the updated list. Finally we show a success message to the user.
Now let's think about how we can implement the form form creating new tasks. It would be better if we create a new component for this form so that we can reuse it later for implementing the update functionality of tasks. Also components should not contain too much code.
Lets create a new file src/components/EditTask.js and add the following basic component code to it.
The code shows a basic HTML form with a submit button and one input field for the task's description. It is your turn now to add the missing fields and add styling to the HTML code. Have a look at Bootstrap's form classes.
function getTasks() {...} // Retrieve tasks from the API
function getTask(id) {...} // Retrieve a single task from the API by its ID
function createTask(task) {...} // Send a POST request to the API for creting a new task entity
function updateTask(task) {...} // Send a PUT request to the API for updating an existing entity
function deleteTask(id) {...} // Send a DELETE request to the API for deleting a task entity
import * as api from '../services/api';
/**
* Tasks component
*/
const AthleteTasks = () => {
const [tasks, setTasks] = useState([]); // Here we store our tasks from the API
const [loaded, setLoaded] = useState(false); // This boolean variable indicates if the tasks have been loaded already
/**
* Fetch tasks from the API and store them in the internal state of the component.
*/
const loadTasks = () => {
// Fetch tasks from the API
const _tasks = api.getTasks();
// Save tasks in the interbal state of the component
setTasks(_tasks);
// Set variable 'loaded' to true
setLoaded(true);
};
// Load tasks from the API of they haven't been loaded before
if (!loaded) {
loadTasks();
}
};
/**
* Tasks component
*/
const AthleteTasks = () => {
// ...
/**
* Send a delete request to the API.
*
* @param id The id o fthe task that should be deleted
*/
const deleteTask = (task_id) => {
// Send a DELETE request to the API
api.deleteTask(task_id);
// Reload tasks
loadTasks();
// Show an alert
window.alert("Task deleted successfully!)
};
// ...
};
/**
* Tasks component
*/
const AthleteTasks = () => {
// ...
/**
* Send a POST request to the API.
*
* @param task A task object
*/
const createTask = (task) => {
// Send a POST request to the API
api.createTask(task);
// Reload tasks
loadTasks();
// Show an alert
window.alert("Task created successfully!)
};
// ...
};
const EditTask = (props) => {
// This state variable holds the task that we want to create / update
const [newTask, setNewTask] = useState({
description: '',
link: '',
due: new Date(),
});
// Render the form
return (
<Fragment>
<form>
{/* Input field for the task description */}
<input
id='task_description'
value={newTask.description}
type='text'
placeholder='description'
onChange={(e) => {
setNewTask({ ...newTask, description: e.target.value });
}}
/>
{/* Submit button to send the form data to the API */}
<button
className='btn btn-primary mt-3'
onClick={(e) => {
onFormSubmit(e);
}}
>
Submit
</button>
</form>
</Fragment>
);
};