API responses are similar to receiving a reply from a friend when you ask a question. This reply, known as an API Response, is packed with useful information: the data you asked for, a status code (a mini report on how the request went), headers (like additional information about the data), and more.
Most often, the data returned by APIs is in a format called JSON (JavaScript Object Notation), an easy to use, neatly organized data format.
When fetching data, JavaScript's Fetch API offers a Promise. Once resolved, it provides a Response object. An example API and its response can be seen as follows:
JavaScript1fetch('https://api-regional.codesignalcontent.com/posting-application-2/posts') 2 .then(response => console.log(response)) // We log the whole response here 3 .catch(error => console.error('Error:', error));
The fetch
function takes an API URL, then returns a Promise. The Promise then resolves to present a Response object, which is logged to the console.
API requests can return varying outcomes, leading to success, a resource being not found, a server error, and so on. These statuses of an API request are denoted by HTTP status codes, like a mini report card for our API request.
HTTP status codes are grouped into five classes:
- 1xx (Informational): The API received the request, and the process is continuing.
- 2xx (Successful): The request was successfully received, understood, and accepted.
- 3xx (Redirection): Extra action must be taken to complete the request
- 4xx (Client Error): The request has bad syntax or cannot be fulfilled.
- 5xx (Server Error): The server failed to complete a valid request.
We can fetch the status code of an API response like this:
JavaScript1fetch('https://api-regional.codesignalcontent.com/posting-application-2/posts') 2 .then(response => console.log(response.status)) // We now log only the status code 3 .catch(error => console.error('Error:', error));
With API requests, errors may occur. Just like we handle adversities in our lives, we need to handle these errors in our code. We can handle these errors in JavaScript using the try...catch
statement, which is like saying, "Try this, but if there's a problem, don't panic, do this instead."
Let's see how to apply this when making API requests:
JavaScript1try { 2 // We're attempting to make an API request 3 const response = fetch('https://api-regional.codesignalcontent.com/posting-application-2/posts') 4 // If response is not Ok, we throw an Error 5 if (!response.ok) { 6 throw new Error('API request failed, status: ' + response.status) 7 } 8} catch (error) { 9 // If any error occurs, we catch it and log it 10 console.error('Error occurred: ', error); 11}
In the code above, "if the response is not Ok (meaning, if the status code does not start with 2), we throw an error which is later caught and logged.
Note that the error
on line 6 refers to a situation where we intentionally throw
an error if the API request is not successful. In this case, if the HTTP status code of the response is not 2xx (meaning the request is not successfully processed), we throw a new Error
with a custom message API request failed with status ' + response.status
. This is our way of handling unsuccessful API requests and providing a meaningful error message.
On the other hand, the error
on line 8 refers to any unforeseen error that happens within the try
block. This could range from network issues, coding errors, to unexpected application behaviour. The catch
block catches any exception or error that occurs in the try
block and executes statements within the catch
block. Here we're catching that error and logging it to console. This ensures our code doesn't stop running unexpectedly, and we're informed about any errors that occur.
Timely feedback immensely improves user experience, especially when waiting for data to be fetched and displayed. This ensures users are aware of the application's responsiveness. Loading indicators are our friend, ensuring users know that action is being taken.
Let's use the example API https://api-regional.codesignalcontent.com/posting-application-2/posts
and see how we effectively employ loading states:
JavaScript1// Set 'Loading...' message while fetch is in progress. 2document.getElementById('posts').textContent = 'Loading...'; 3 4fetch('https://api-regional.codesignalcontent.com/posting-application-2/posts') 5 .then(response => { 6 // Check if the response is Ok 7 if (!response.ok) { 8 throw new Error(`HTTP error! status: ${response.status}`); 9 } else { 10 return response.json(); 11 } 12 }) 13 .then(data => { 14 // Display fetched data in JSON format 15 document.getElementById('posts').textContent = JSON.stringify(data); 16 }) 17 .catch(error => { 18 // Log error message and inform user if fetch operation fails. 19 console.log('There has been a problem with the fetch operation: ', error.message); 20 document.getElementById('posts').textContent = 'Data Unavailable.'; 21 });
In the example above, if there's an error during the fetch operation, using a .catch
block allows us to capture the error, log an error message, and inform the user with 'Data Unavailable.' Hence, the user is always informed about the application's status.
In JavaScript, JSON.stringify()
is a function that converts a JavaScript object or value to a JSON string. This function can be quite useful when you need to send or store the data as JSON, or when you want to log an object's contents in a readable format.
Here's a simple example:
JavaScript1const car = { 2 name: "Tesla Model 3", 3 price: "$35,000", 4 weight: "1,611 kg", 5}; 6 7console.log(JSON.stringify(car));
In this example, we have a car
object. When we call JSON.stringify(car)
, it converts the car
object into a JSON string:
JavaScript1'{"name":"Tesla Model 3","price":"$35,000","weight":"1,611 kg"}'
You'll notice that the keys and string values are surrounded by quotes, just like in a normal JSON object. This is something that JSON.stringify()
does automatically.
This function is also useful for handling API data because APIs often send data as JSON strings. If you want to log the API data or store it somewhere, it's usually more convenient to handle it as a JSON string than as a JavaScript object. That's where JSON.stringify()
comes in handy! It prepares your JavaScript data for sending to an API or for logging.
We've taken a deep dive into API responses, understanding their structure, HTTP status codes, their categories, and most importantly, we learned how to handle errors when making API requests.
In the next segment of our API adventures, we're going to dive deeper into data processing and JSON transformation. Get ready to enhance your skills in managing and manipulating data in various formats. We're excited to guide you through these essential aspects of working with APIs. Don't forget to practice the exercises to consolidate your newfound knowledge. As always, remember that practice is key to mastery in both life and coding. Happy coding!