DEV Community

Cover image for Rethinking the Fetch API: Strengths and Shortcomings
Amin
Amin

Posted on

Rethinking the Fetch API: Strengths and Shortcomings

I like the Fetch API.

During my time in school, I wrestled with the XMLHttpRequest, a technology from a bygone era, and I'm genuinely glad to have crossed paths with this "new" Fetch API, especially as I approached the finish line of my academic journey. I had a hunch that I would be working more with the Fetch API than the XMLHttpRequest in my professional career, and that intuition turned out to be right.

Why am I so content with this transition? Well, let's talk about the XMLHttpRequest first.

The XMLHttpRequest, while once a workhorse, does have its fair share of design quirks. One significant drawback is its inability to handle promises, which can make life quite challenging when you're dealing with complex scenarios that involve dependent requests. Remember those callback functions? They can get quite unwieldy, leaving much to be desired in terms of developer experience.

Here's an interesting revelation: you can even cancel a request using the XMLHttpRequest! However, does this mean the Fetch API is inherently superior?

Yes and no.

Absolutely yes, because the Fetch API is tailored for intricate scenarios, leveraging modern data structures to handle potential future states. It significantly enhances the developer experience by employing Promises, empowering us to craft code in a more imperative style with Asynchronous Functions. Not to mention its user-friendly approach to sending requests.

You might be thinking, "This sounds perfect, what could possibly go wrong?"

Well, it's not all sunshine and rainbows. The Fetch API, like its predecessor XMLHttpRequest, carries certain flaws that have yet to be addressed.

Status Code

HTTP status codes are like the storytellers of the internet. They narrate the journey of a request as it embarks on an adventure through the digital landscape. Let's dive into this narrative by considering a common scenario: requesting a list of all users.

Imagine a user who's excited to get this list. But things can take a wrong turn along the way. Here are a few potential plot twists:

Unauthorized Request (401): Sometimes, a user might be disconnected or using an invalid token. In HTTP terms, this is a 401 status code. It's the server's way of saying, "Sorry, but you're not allowed here."

Forbidden Request (403): Another possibility is that the user does not have the necessary rights to access the list of users. This is when a 403 status code comes into play. The server firmly states, "Access denied."

Not Found Request (404): Lastly, the user might be mistaken, thinking they can find the list of users at a particular URL, like https://api.domain.com/users. However, if the list has relocated to https://api.domain.com/administration/users, a 404 status code is returned. The server kindly informs the user, "Sorry, but what you're looking for isn't here."

There are numerous other scenarios where things can go haywire, but here's the kicker: for the Fetch API, encountering a 404, 401, or 403 response status isn't necessarily considered an error. It's all about perspective. However, in the realm of the HTTP protocol, these are indeed error codes.

The catch here is that you must handle these status codes properly, or you risk your application crashing while thinking everything is going smoothly.

Network

So, now that we've got the hang of handling status codes, everything should be smooth sailing, right? Well, not quite.

Imagine this scenario: you're waiting for a response from the server, and suddenly, a mischievous shark decides to take a bite out of the one cable carrying your precious data. Now, all you're left with is a vague, generic error message.

Or picture this: you're on a bus, passing through a city that temporarily has no internet connection. Again, all you get is an error, leaving you in the dark.

And then there's the classic "offline" situation. Yep, you guessed it – that's an error too.

All these scenarios fall under the category of network errors. But here's the kicker: for the Fetch API, they're simply lumped together as errors, just like the ones you might intentionally throw in your domain-specific functions.

So, what's the challenge here? How can you distinguish between errors originating from the Fetch API and those that stem from your own code?

Response

After sifting through hundreds of lines of code, you've diligently handled all possible error codes your server might throw your way. You've even considered scenarios where your request might not make it at all. You've put in the hard work to ensure your application can weather the storm of potential server hiccups.

But what if the story took an unexpected twist? Imagine waiting for a list of users as an array, only to have the server send you an object containing the list of users. In a typical scenario, this could lead to a spectacular crash. Why? Because you've made an assumption – you've assumed your server will remain unchanged, even though the only constant in the digital world is change itself.

This assumption is the risk you're taking right now. Presently, everything seems to work flawlessly; you're building your application with data that fits like a glove. But there's a nagging reality: this might not be the case in a month, a year, or a decade.

The Quirks in Fetch API Design

Now, I don't have the authority to pass harsh judgments. But let's consider how other programming languages, such as Elm, with only a fraction of JavaScript's popularity, approach HTTP requests.

Elm takes a fresh perspective on this issue, emphasizing a lack of side effects. It has a built-in ability to handle these five cases correctly.

When you make a request, it's essential to account for all potential success and error scenarios. The errors can manifest as:

Bad URL: Strings can be tricky, and your server might hiccup when dealing with malformed URLs like https://api.domain.com or domain.com/http://api.

Timeout: Sometimes, a request might never receive a response, especially if the server's code leaves something to be desired.

Network Error: The mysterious realm of the internet holds its secrets, and network errors can occur for a variety of reasons.

Bad Status Code: While the Fetch API may not consider a bad status code an error, in the HTTP protocol, it's classified as an error.

Bad Body: Parsing the response body is vital, but we can't predict when the server might change the structure of the response.

Redesigning Fetch

I would love to see a Fetch API that would improve the developer experience by enabling more reliable and precise errors.

let error = null;
let users = [];

async function getUsers() {
  try {
    users = [];
    error = null;

    const response = await fetch("https://jsonplaceholder.typicode.com/users", {
      validation: (body) => {
        return Array.isArray(body) && body.map(user => {
          return typeof user === "object" && typeof user.id === "string";
        });
      }
    });

    const receivedUsers = await response.json();

    users = receivedUsers;
  } catch (error) {
    if (error instanceof FetchCancelError) {
      error = "The request has been canceled";
    } else if (error instanceof FetchNetworkError) {
      error = "Something went wrong, are you offline?";
    } else if (error instanceof FetchTimeoutError) {
      error = "The request took too much time, please try again later";
    } else if (error instanceof FetchStatusError) {
      if (error.status === 401) {
        error = "You must be authenticated before displaying the resource";
      } else if (error.status === 403) {
        error = "You cannot display this resource";
      } else {
        error = "An error occurred, please try again later";
      }
    } else if (error instanceof FetchValidationError) {
      error = "Bad response from the server";
    } else {
      error = "Unknow error";
    }
  }
}

await getUsers();
Enter fullscreen mode Exit fullscreen mode

A Dream That Can Never Be

In my final thoughts, I can't help but dream. I dream of an ideal Fetch API that would make error handling a breeze, ensuring developers never miss a beat. However, it's important to acknowledge the stark reality: this dream is more of a wistful wish.

The Fetch API, as it stands, has been woven into the fabric of web development. It's not a blank canvas waiting to be painted with new design strokes. Any attempt to overhaul it would risk backward incompatibilities and could potentially shake the very foundations of the web as we know it.

So, as I share this dream, I do so with the understanding that it may forever remain an aspiration, a vision of what could have been. The Fetch API, with its strengths and quirks, is here to stay, and we, as developers, must adapt and make the best of the tools at our disposal.

That said, it's worth noting that the development community is not without alternatives. There are libraries like Axios, which have been designed to address some of the very concerns I've mentioned. These libraries, in many ways, bridge the gap between my dream and the practical realities of web development. They provide powerful tools for handling HTTP requests and responses in a more developer-friendly and robust manner.

While the dream of a perfectly refined Fetch API may linger in the background, we continue to navigate the ever-evolving landscape of web development with the tools we have today. And who knows, maybe the future holds a different dream waiting to be realized. Until then, let's keep building, adapting, and learning in this dynamic world of code.

Top comments (0)