Working with the Fetch API
How to Implement a Fetch Wrapper?
Section titled “How to Implement a Fetch Wrapper?”To create a reusable fetch wrapper that accepts request options as inputs, we can define a function that abstracts the basic logic of making a fetch request. This wrapper can handle common tasks like error handling, custom headers, or any default configurations, and will allow you to pass in the full set of options as needed.
Advantages of the Fetch Wrapper
Section titled “Advantages of the Fetch Wrapper”The wrapper should be implemented as an ES6 module. This means that the code is stored in a file named fetchWrapper.js and can be imported into other files using the import statement.
- You can use this ES module throughout your application for any type of HTTP request.
- Error handling is in one place, which makes your code easier to maintain and debug.
- Set defaults for common parameters like
method,headers, andmode, while still allowing customization when needed. - JSON responses are parsed automatically, with graceful handling of non-JSON content like plain text or HTML.
Fetch Wrapper Example
Section titled “Fetch Wrapper Example”Here’s a basic example of such a wrapper implemented as an ES module. The following code must be stored in a file named fetchWrapper.js:
/** * A wrapper class for making HTTP requests using the Fetch API. * Provides a method to send HTTP requests with default and custom options. */export class FetchWrapper {
/** * Sends an HTTP request using the Fetch API. * * @param {string} uri The URI identifying the targeted resource to which the request will be sent. * @param {Object} [options={}] Optional configuration object for the request. * @returns {Promise<Object>} A promise that resolves with the parsed JSON response body from the server. * @throws {Error} Throws an error if the request fails (network errors or non-2xx HTTP status codes). * * @example * const fetchWrapper = new FetchWrapper(); * try { * const data = await fetchWrapper.sendRequest('https://api.tvmase.com/shows'); * console.log(data); * } catch (error) { * console.error('Request failed:', error); * } */ async sendRequest(uri, options = {}) {
//* Default options to apply to every request. //* NOTE: These options can be overridden. const defaultOptions = { method: 'GET', cache: 'no-cache' } //* Merge default options with the supplied custom options. const requestOptions = { ...defaultOptions, ...options, headers: { ...defaultOptions.headers, ...options.headers, } }; try { //* Send the fetch request const response = await fetch(uri, requestOptions);
//* Check if the request was successful (status in the range 200-299) if (!response.ok) { // Handle non-success responses (e.g., 404, 500) //!NOTE: Retrieve any error details from the response body for further processing. const errorInfo = await response.json();
//TODO: You could implement a separate function for throwing different types exceptions based on the received status code.
//! Throw a custom exception containing the retrieved error details from. throw new CustomError(`Request failed with status ${response.status}`, response.status, errorInfo); //throw new Error(`Request failed with status ${response.status} reason: ${response.statusText}`); } //* Parse the response body as JSON (assuming JSON content) //* If the response doesn't contain JSON, this will throw an error. return await response.json(); } catch (error) { // Handle network or other errors console.error('Error during fetch operation:', error); throw error; // Rethrow or return an error object as per your needs } }}export class CustomError extends Error { constructor(message, code, details) { super(message); // Call the parent constructor with the message this.name = this.constructor.name; // Set the error name to CustomError this.code = code; // Custom error code this.details = details; // Additional details or context
// Optional: Ensure the stack trace is correct for this subclass if (Error.captureStackTrace) { Error.captureStackTrace(this, this.constructor); } }}How it Works:
Section titled “How it Works:”-
URI and Options: The
sendRequestmethod accepts two parameters:uri: The endpoint you’re sending the request to.options: An object that contains additional options for the request (e.g.,method,headers,body, etc.). It is optional and can be provided to customize the behavior of the request.
-
Default Options:
- The function sets some default values for options like
method,headers. - The
headersobject is merged, allowing you to override the default headers, likeContent-Type, with your own headers if needed.
- The function sets some default values for options like
-
Error Handling:
- The function checks if the response is successful (
response.ok). If not, it throws an error with the response status. - If there’s an issue parsing the response body as JSON (e.g., the server returns HTML or plain text), it throws an error.
- The
try-catchblock around thefetchensures that any network-related errors (e.g., timeout, no connection) are also handled gracefully.
- The function checks if the response is successful (
Usage: Importing the FetchWrapper Module
Section titled “Usage: Importing the FetchWrapper Module”To import the FetchWrapper class in another JavaScript file, you can use the following import statement:
import { FetchWrapper } from './path/to/fetchWrapper';Make sure to replace './path/to/fetchWrapper' with the correct relative path to where the FetchWrapper class is defined. For example, if the file is in the same directory, you would write:
import { FetchWrapper } from './fetchWrapper';Usage: Example of a GET request
Section titled “Usage: Example of a GET request”Sending a GET request with default options:
async function fetchData() { try { const uri = 'https://api.example.com/data'; const fetchWrapper = new FetchWrapper(); const data = await fetchWrapper.sendRequest(uri); console.log(data); // Process the response data } catch (error) { console.error('Fetch error:', error); }}Usage: Example of a POST request
Section titled “Usage: Example of a POST request”You can pass an options object to the sendRequest method to customize the request. The options object can contain the following properties (as shown in the example below):
method: The HTTP method (POST,PUT, etc.) to use for the request.headers: An object containing the headers to be sent with the request (Content-Type,Accept,Authorization, etc.).body: The body of the request. This can be a JSON string, a form data object, or a Blob object.
// Sending a POST request with custom request options including headers and bodyasync function postData() { try { //NOTE: The body of the request is the JSON data that will be sent to the server.
//TODO: You need to use the DOM API to: //------------------------------------- // 1) get the data from the form fields. // 2) Store values you retrieved in a JavaScript object or array of objects, depending on the structure of the data required to be passed as input when your create new resources (POST request). // 3) Convert the JavaScript object or array to a JSON string using JSON.stringify().
//NOTE: This is a simple example of a JSON object. You need to REPLACE IT.Use the DOM API to get the data from the form fields and store it in a JavaScript object or array of objects. const person = { firstName: 'Ladybug', lastName: 'Red', age: 25 }; const uri = 'https://api.example.com/submit'; // Request options const options = { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'Authorization': 'Bearer token123', }, body: JSON.stringify(person), } const fetchWrapper = new FetchWrapper(); const response = await fetchWrapper.sendRequest(uri, options); console.log(response); // Process the response } catch (error) { console.error('Post request error:', error); }}