Top 10 Fetch API JavaScript Solutions for Efficient Data Retrieval

Jennie Lee
7 min readApr 3, 2024

--

Looking for a Postman alternative?

Try APIDog, the Most Customizable Postman Alternative, where you can connect to thousands of APIs right now!

Introduction

In today’s web development landscape, making API calls is a common task. Whether you’re fetching data from a remote server or sending data to it, a module that efficiently handles API calls can greatly improve the organization and readability of your code. The Fetch API, introduced in ES6, provides a modern way to perform HTTP requests in JavaScript. In this tutorial, we will explore how to create a reusable module for handling API calls using the Fetch API.

The Fetch API is based on the concept of promises, making it a powerful tool for handling asynchronous operations. Promises allow for a more elegant and readable code flow by chaining methods and handling success or failure conditions. By leveraging the Fetch API and its promise chains, we can create a module that simplifies the process of making API calls and handling responses.

Creating a Fetch.js Module Skeleton

Before diving into the implementation details, let’s first set up the basic structure of the Fetch.js module. In this module, we will define four public methods: get, create, update, and remove, which correspond to the CRUD operations commonly performed on data.

To start, let’s define the API host, which is the URL of the server we are making API calls to. This can be set as a constant variable within our module. Additionally, we will define the skeleton of our Fetch.js module, which will export the four CRUD methods.

const API_HOST = 'https://api.example.com';

const Fetch = {
async get(endpoint, params) {
// Fetch GET implementation
},

async create(endpoint, data) {
// Fetch POST implementation
},

async update(endpoint, data) {
// Fetch PUT implementation
},

async remove(endpoint) {
// Fetch DELETE implementation
},
};

export default Fetch;

Creating the Main Fetch Method

The main Fetch method is responsible for performing the actual HTTP request using the Fetch API. This method will be used by the public methods of our module to make API calls.

To implement the Fetch method, we will create a private function inside our Fetch.js module. This function will take three arguments: the API endpoint, additional parameters, and the request method. Inside the function, we will use the Fetch API to make the HTTP request and return the response.

const Fetch = {
// ...

async fetchResource(url, params, method) {
const options = {
method,
headers: {
'Content-Type': 'application/json',
},
};

// Perform the fetch request
const response = await fetch(url, options);

// Return the response
return response;
},
};

export default Fetch;

Here, we define an options object that includes the request method (e.g., GET, POST, PUT, DELETE) and Content-Type header. The fetch function is then called with the url and options. Finally, the function returns the response object.

It’s important to note that the Fetch API returns a promise, which resolves to the response object. We utilize the await keyword to wait for the promise to settle before proceeding. This allows for cleaner and more readable code compared to traditional callback-based approaches.

Adding Options and Using Await

Next, let’s explore additional options for the fetch request, such as custom headers and request methods other than GET. This will allow us to cater to a wider range of API requirements.

const Fetch = {
// ...

async fetchResource(url, params, method) {
const options = {
method,
headers: {
'Content-Type': 'application/json',
// Additional headers can be added here
},
// Additional options, such as credentials and mode, can be added here
};

// Convert params to query string for GET requests
if (method === 'GET' && params) {
const queryString = Object.keys(params)
.map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
.join('&');

url += `?${queryString}`;
}

// Convert params to JSON for other requests
if (method !== 'GET' && params) {
options.body = JSON.stringify(params);
}

const response = await fetch(url, options);

return response;
},
};

In the updated code snippet, we extend the options object to include any additional headers or options required by the API. For example, you can include authentication-related headers or set the credentials property to include for cookies.

Additionally, we handle parameters differently based on the request method. For GET requests, we transform the parameters into a query string and append it to the URL. For other request methods (POST, PUT, DELETE), we convert the parameters to JSON and include them in the request body.

Handling Parameters for All Request Types

In the previous step, we handled parameters differently based on the request method. However, handling parameters can vary even within the same request type, depending on the API requirements. For example, an API might expect parameters to be sent as form data instead of JSON.

To handle this flexibility, we can add another parameter to our Fetch.js module’s public methods. This parameter will allow the consumer of the module to specify the content type of the parameters being sent.

const Fetch = {
// ...

async fetchResource(url, params, method, contentType) {
// ...

if (method === 'GET' && params) {
// ...
}

if (method !== 'GET' && params) {
// ...
if (contentType === 'form') {
options.headers['Content-Type'] = 'application/x-www-form-urlencoded';
options.body = new URLSearchParams(params);
}
}

// ...
},
};

Here, we introduce the contentType parameter in the fetchResource method. If the consumer specifies the contentType as form, we change the Content-Type header to application/x-www-form-urlencoded and use the URLSearchParams API to transform the parameters into a URL-encoded string.

This approach gives developers the flexibility to handle various parameter types and fulfill different API requirements within the module.

Creating Public Methods

Now that we have implemented the main Fetch method, we can create the public methods of our Fetch.js module. These methods, namely get, create, update, and remove, will call the main Fetch method with the appropriate parameters based on their respective CRUD operations.

const Fetch = {
async get(endpoint, params, contentType) {
const url = `${API_HOST}${endpoint}`;
const response = await this.fetchResource(url, params, 'GET', contentType);
const data = await response.json();
return data;
},

async create(endpoint, data, contentType) {
const url = `${API_HOST}${endpoint}`;
const response = await this.fetchResource(url, data, 'POST', contentType);
const data = await response.json();
return data;
},

async update(endpoint, data, contentType) {
const url = `${API_HOST}${endpoint}`;
const response = await this.fetchResource(url, data, 'PUT', contentType);
const data = await response.json();
return data;
},

async remove(endpoint, contentType) {
const url = `${API_HOST}${endpoint}`;
const response = await this.fetchResource(url, null, 'DELETE', contentType);
return response.ok;
},
};

export default Fetch;

In these public methods, we construct the endpoint URL by combining the API host and the provided endpoint. We then call the main fetchResource method with the appropriate request method and parameters. Finally, we extract the JSON data from the response using the json method and return it.

Having these public methods encapsulated within the Fetch.js module allows us to easily make API calls in a more readable and modular manner.

Error Handling

When making API calls, it is important to handle errors appropriately. Error handling ensures that your application gracefully handles faulty requests and provides meaningful feedback to the user.

In our Fetch.js module, we can check the status code of the response to determine if the request was successful or if there was an error.

// ...

const Fetch = {
// ...

async fetchResource(url, params, method, contentType) {
// ...
const response = await fetch(url, options);

// Check the status code
if (!response.ok) {
const error = {
status: response.status,
message: response.statusText,
};
throw error;
}

return response;
},

// ...
};

In this updated code snippet, if the status code of the response is not within the 200 range, we throw an error object with the status code and status text. This allows the consumer of our module to handle errors appropriately.

Usage and Next Steps

Now that we have created our Fetch.js module, let’s explore some examples of how we can use it for different types of API requests.

import Fetch from './Fetch';

const fetchModule = Fetch;

// GET request
const getData = async () => {
try {
const response = await fetchModule.get('/data');
console.log(response);
} catch (error) {
console.error(`Error: ${error.message}`);
}
};

getData();

// POST request
const createData = async () => {
try {
const response = await fetchModule.create('/data', { name: 'John Doe', age: 30 });
console.log(response);
} catch (error) {
console.error(`Error: ${error.message}`);
}
};

createData();

// PUT request
const updateData = async () => {
try {
const response = await fetchModule.update('/data/1', { name: 'Jane Doe', age: 25 });
console.log(response);
} catch (error) {
console.error(`Error: ${error.message}`);
}
};

updateData();

// DELETE request
const deleteData = async () => {
try {
const response = await fetchModule.remove('/data/1');
console.log(response);
} catch (error) {
console.error(`Error: ${error.message}`);
}
};

deleteData();

In this example, we import and use the fetchModule from our Fetch.js module. We call the appropriate public methods (get, create, update, remove) with the required parameters. The methods handle the API calls using the Fetch API and return the response or throw an error if applicable.

Next steps for customization and enhancement include configuring custom headers, handling file uploads, implementing pagination, and integrating middleware for global error handling.

In conclusion, creating a reusable module for handling API calls using the Fetch API in JavaScript can greatly improve code organization and readability. By implementing a module that encapsulates the Fetch API functionality, developers can make API calls and handle responses more efficiently. This tutorial covered the process of creating such a module step-by-step, from setting up the basic structure to handling parameters and errors. We also explored examples of how to use the module for different API requests. With this module, you can enhance productivity, improve code quality, and lay the groundwork for more scalable and maintainable applications. So go ahead, experiment with the module, and explore the possibilities it offers!

Looking for a Postman alternative?

Try APIDog, the Most Customizable Postman Alternative, where you can connect to thousands of APIs right now!

--

--

Jennie Lee
Jennie Lee

Written by Jennie Lee

Software Testing Blogger, #API Testing

No responses yet