Reza Savadkouhi

JavaScript – Dealing with REST API using async/await
Introduction

In this exercise, you will use JavaScript to fetch data from a REST API and manipulate the data using asynchronous functions. The API we will use is jsonplaceholder.typicode.com, which provides a collection of mock JSON data for web development.

Our task is to fetch all users from the API, including their posts and todos, and return an array of users with the desired keys: ID, username, email, and phone. Additionally, return the user’s posts (title and body) and todos (title) for each object.

Prerequisites
  • Basic knowledge of JavaScript
  • Familiarity with REST API
  • Knowledge of JSON structure
  • Knowledge of ES6 features
Understanding jsonplaceholder.typicode.com

jsonplaceholder.typicode.com is a popular service that provides mock data for web development. It offers a variety of endpoints that return JSON data representing different types of data structures, such as users, posts, and comments.

The user endpoint provides a collection of mock users, each with an ID, username, email, phone, and other relevant properties. We will be using this endpoint to fetch data for our exercise.

Code Breakdown

Let’s break down the code step by step:

HTML
<p>Click to get users from the source. Check console for the result.</p>
<!-- Using onclick -->
<button onclick="printUsersData()">Get Users' Data</button>

The <button onclick="printUsersData()">Get Users' Data</button> code snippet creates a button element with the text “Get Users’ Data” and attaches an onclick event listener to the button. When the button is clicked, the printUsersData() function is called to fetch and print the users’ data from the API.

JavaScript
// Function to fetch all users
// Using async/await to handle promises for fetching from the API
const getUsers = async () => {
  try {
    // Fetch users' data from the API
    const response = await fetch('https://jsonplaceholder.typicode.com/users');
    const users = await response.json();

    // Map users data to the desired format
    const formattedUsers = users.map(({ id, username, email, phone }) => ({
      id,
      username,
      email,
      phone,
    }));

    // Fetch and add posts and todos for each user
    const usersWithPostsAndTodos = await Promise.all(
      formattedUsers.map(async (user) => {
        const [postsResponse, todosResponse] = await Promise.all([
          fetch(`https://jsonplaceholder.typicode.com/users/${user.id}/posts`),
          fetch(`https://jsonplaceholder.typicode.com/users/${user.id}/todos`),
        ]);

        // Fetch user's posts and todos
        const posts = await postsResponse.json();
        const todos = await todosResponse.json();

        // Map posts and todos to the desired format
        const formattedPosts = posts.map(({ title, body }) => ({
          title,
          body,
        }));

        const formattedTodos = todos.map(({ title }) => ({ title }));

        // Add posts and todos to the user object
        return {
          ...user,
          posts: formattedPosts,
          todos: formattedTodos,
        };
      })
    );

    // return all users with their posts and todos in an array
    return usersWithPostsAndTodos;
  } catch (error) {
    console.error('Error:', error);
    throw error;
  }
};

// Function to print users' data to the console
const printUsersData = () => {
  getUsers()
    .then((users) => {
      console.log(users);
    })
    .catch((error) => {
      console.error('Error:', error);
    });
};
Asynchronous Functions in Modern JavaScript

Asynchronous programming is a programming paradigm that allows us to execute tasks without waiting for the previous one to complete. In modern web development, asynchronous functions are a fundamental aspect of JavaScript. Asynchronous functions allow us to handle operations that take time to complete without blocking the main thread. This feature is handy when dealing with time-consuming tasks such as fetching data from a server or loading large image files. Using asynchronous programming, we can build responsive and performant applications that don’t freeze or become unresponsive.

One way to handle asynchronous operations is to use the .then() and .catch() methods. These methods are used to chain asynchronous operations together, ensuring that each operation is executed only after the previous one has completed successfully.

However, there is a more concise and intuitive way to handle asynchronous operations in modern JavaScript using the async/await syntax. The async/await syntax allows us to write asynchronous code as if it were synchronous, using keywords like await to suspend execution until an asynchronous operation has completed.

One way to handle asynchronous operations is to use the .then() and .catch() methods. These methods are used to chain asynchronous operations together, ensuring that each operation is executed only after the previous one has completed successfully.

However, there is a more concise and intuitive way to handle asynchronous operations in modern JavaScript using the async/await syntax. The async/await syntax allows us to write asynchronous code as if it were synchronous, using keywords like await to suspend execution until an asynchronous operation has completed.

Retrieving Users’ Data

The getUsers() function is responsible for retrieving all users from the API and returning an array of formatted user objects. It uses the async/await syntax to handle the asynchronous operations involved in fetching and processing the data.

The getUsers() function first fetches the JSON data from the https://jsonplaceholder.typicode.com/users endpoint using the fetch() method. Then, it converts the JSON data to a JavaScript object using the response.json() method.

Next, the function maps the user data to the desired format, including the user’s ID, username, email, and phone.

Finally, the function fetches the user’s posts and todos using the fetch() method. It then maps the posts and todos to the desired format and adds them to the corresponding properties of the user object.

The await Promise.all() method is used to wait for all of the asynchronous operations to complete before returning the final array of users with their posts and todos.

Promises in JavaScript

Promises are a fundamental concept in JavaScript that enable asynchronous code execution. They are used to handle situations where the result of an operation is not available immediately, such as when making an API call or performing a complex calculation. Promises allow you to chain multiple asynchronous operations together, ensuring that dependent operations are not executed until all preceding operations have completed successfully.

For instance, when making an API call, the response time can vary depending on the network conditions. Promises provide a way to handle such delays by allowing you to chain multiple asynchronous operations together. By doing so, you can ensure that the dependent operations are not executed until all preceding operations have been completed successfully.

In essence, promises provide a mechanism to manage complex code requiring multiple asynchronous tasks to be executed sequentially. By using promises, developers can write cleaner and more maintainable code that is easier to reason about and debug.

Promise.all

The Promise.all method is used to wait for multiple promises to resolve before proceeding. It takes an array of promises as an argument and returns a single promise that resolves when all of the specified promises have resolved.

Retrieving Posts and Todos

The getUsers() function uses the Promise.all() method to fetch the user’s posts and todos concurrently. This improves the performance of the code by making multiple requests simultaneously.

The fetch() method is used to fetch the posts and todos from their respective endpoints. The Promise.all() method is used to wait for both requests to complete before processing the data.

The data from the posts and todos endpoints is then mapped to the desired format and added to the corresponding properties of the user object.

Using Map()

The map() function is used to iterate over an array and transform each element into a new element. In this case, the map() function is used to transform the user objects to add their posts and todos.

The formattedPosts and formattedTodos objects are created by mapping the posts and todos data to the desired format. The title property of each post and todo is extracted and used to create a new object with only the title property.

Using …user

The spread operator (...user) in JavaScript is useful for copying objects. Specifically, when used with user objects, it allows for creating a new object that contains all of the original object’s properties plus any additional properties that may have been added. This feature is handy when working with complex data structures that require frequent copying and modification. Notably, the original user object is left unchanged, ensuring data integrity and consistency throughout the code.

Handling Errors with Try/Catch and Throw

In asynchronous programming, it’s crucial to handle errors gracefully to ensure the stability and usability of your application. JavaScript provides two mechanisms for handling errors: try/catch and throw.

The try/catch block allows you to execute code that may potentially throw an error. If an error occurs within the try block, the code execution is immediately suspended, and the control is transferred to the corresponding catch block. The catch block can then handle the error appropriately, such as logging the error message or displaying a user-friendly error message.

The throw keyword is used to explicitly throw an error. This can be useful for signaling an unexpected condition or indicating a failure in the code execution. When an error is thrown, the current execution is halted, and the error object is passed to the catch block of the nearest enclosing try/catch block.

In the provided code, the getUsers() function uses the try/catch block to handle errors that may occur during the asynchronous operations. If an error occurs, the catch block logs the error message and rethrows the error to propagate it further up the call stack.

The printUsersData() function uses the await keyword to wait for the getUsers() function to complete before printing the users data. If an error occurs during the getUsers() function, it will be caught by the then() callback function and logged to the console.

Conclusion

This exercise demonstrates the use of asynchronous functions in modern JavaScript to fetch data from a REST API and manipulate the data. The async/await syntax makes it easier to write code that is more concise and easier to read.

By understanding the different approaches to asynchronous programming, you can build more efficient and responsive web applications.

Leave a Comment

Your email address will not be published. Required fields are marked *