Live Demo đ All Demo, No Pitch: Content & Commerce / Builder.io & Elastic Path on 12/13
Use Cases
Pricing
Blog
Ă
Visual CMS
Drag-and-drop visual editor and headless CMS for any tech stack
Theme Studio for Shopify
Build and optimize your Shopify-hosted storefront, no coding required
Resources
Blog
Get StartedLoginâ°
fetch
in JavaScript is awesome.
But, you may have something like this sprinkled throughout your code:
While nice and simple, this code has a number of issues.
You could say âoh, yeah, handle errorsâ, and rewrite it like this:
That is an improvement, certainly, but still has issues.
Here, weâre assuming user
is in fact a user object⌠but that assumes that we got a 200
response.
But fetch
does not throw errors for non-200 statuses, so you could have actually received a 400
(bad request), 401
(not authorized), 404
(not found), 500
(internal server error), or all kinds of other issues.
So, we could make another update:
Now, weâve finally achieved a pretty good usage of fetch
. But this can be a bit clunky to have to remember to write out every time, and youâd have to hope everyone on your team is handling each of these situations every time.
It also isnât the most elegant in terms of control flow. In terms of readability, I personally prefer the problematic code at the beginning of this article (in some ways). It read pretty cleanly - fetch the user, parse to json, do things with user object.
But in this format we have fetch the user, handle a bunch of error cases, pare the json, handle other error cases etc. Itâs a little jarring, especially when at this point we have error handling both above and below our business logic, as opposed to centralized in one place.
A more elegant solution could be to throw
if the request has problems, as opposed to handling errors in multiple places:
But weâre left with one last problem - when it comes time to handle the error, weâve lost a lot of useful context. We canât actually access res
in the catch block, so at the time of processing the error we donât actually know what the status code or body of the response was.
This will make it hard to know the best action to take, as well as leave us with very uninformative logs.
An improved solution here could be to create your own custom error class, where you can forward the response details:
Now, when we preserve the status codes, we can be much smarter about the error handling.
For instance, we can alert the user on a 500
that we had an issue, and to potentially try again or contact our support.
Or if the status is 401
, they are currently unauthorized and may need to log in again, etc.
Iâve got one last issue with our latest and greatest solution - it still depends on the developer to write a decent bit of boilerplate every time. Making changes project-wide, or enforcing that we always use this structure, can still be a challenge.
Thatâs where we can wrap fetch to handle things as we need:
And then we can use it as follows:
In our final example, it would be good to ensure we have a unified way that we handle errors. This may include alerts to users, logging, etc.
Exploring this was fun and all, but itâs important to keep in mind that you donât always have to create your own wrappers for things. Here are some preexisting options that are popular and may be worth using, including some that are under 1kb in size:
Axios is a very popular option for data fetching in JS, which handles several of the above scenarios for us automatically.
My only critique of Axios is it is surprisingly large for a simple data fetching wrapper. So if kb size is a priority for you (which Iâd argue it generally should be to keep your performance top notch), you may want to check out one of the below two options:
If you love Axios, but donât love that itâll add 11kb to your bundle, Redaxios is a great alternative, that uses the same API as Axios, but in less than 1kb.
One newer option, which is a very thin wrapper around Fetch much like Redaxios, is Wretch. Wretch is unique in that it largely still feels like fetch, but gives you helpful methods for handling common statuses which can chain together nicely:
Last but not least, letâs not forget that using fetch
directly can have common pitfalls when sending data via a POST
, PUT
, or PATCH
Can you spot the bug in this code?
There is at least one, but likely two.
First, if we are sending JSON, the body
property must be a JSON-serialized string:
That can be easy to forget, but if we are using TypeScript this can at least be caught for us automatically.
An additional bug, which TypeScript will not catch for us, is that we are not specifying the Content-Type
header here. Many backends require you to specify this, as they will not process the body properly otherwise.
Now, we have a relatively robust and safe solution.
We could decide to add some safety for these common situations in our wrapper as well. For instance with the below code:
And now we can just use our wrapper like so:
Simple and safe. I like.
While it is fun and interesting to define our own abstractions, letâs make sure to point out how a couple popular open source projects also handle these situations for us automatically:
For Axios and Redaxios, code similar to our original âflawedâ code with raw fetch
actually works as expected:
Similarly, with Wretch, the most basic example works as expected as well:
Last, but not least, if you want to implement your own wrapper around fetch
, letâs at least make sure it is type-safe with TypeScript if that is what you are using (and hopefully you are!).
Here is our final code, including type definitions:
When using our shiny new type-safe fetch wrapper, you will run into one last issue. In a catch
block in typescript, by default error
is of any
type
You could say, oh! Iâll just type the error:
Ugh, thatâs right, we canât type errors in TypeScript. Thatâs because technically you can throw
anything in TypeScript, anywhere. The below is all valid JavaScript/TypeScript and could theoretically exist in any try
block
Not to mention that fetch
itself could throw itâs own error that is not a ResponseError
, for example for network errors such as no connection being available.
We could also accidentally have a legitimate bug in on our fetch wrapper that throws other errors like a TypeError
So a final, clean, and type-safe usage of this wrapper, would be something like:
Here, we can check with instanceof
if err
is a ResponseError
instance, and get full type safety within the conditional block for the error response.
And then we can also re-throw an error if any unexpected errors occured, and use the new Error cause property in JavaScript to forward the original error details for nicer debugging.
Finally, it may be nice to not always have to have a custom built switch
for every possible error status for each HTTP call.
It would be nice to encapsulate our error handling into a reusable function, that we can use as a fallback after we handle any one-off cases we know we want special logic for that is unique to this call.
For instance, we may have a common way we want to alert users of a 500 with an "oops, sorry, please contact support" message, or for a 401 a "please log in again" message, as long as there is not a more specific way we want to handle this status for this particular request.
This, in practice, could for example look like:
Which we could implement like:
This is one place I think Wretch shines, as the above code could similarly look like:
With Axios or Redaxios, things look similar to our original example
And there we have it!
If not otherwise clear, I would personally recommend using an off the shelf wrapper for fetch, as they can be very small (1-2kb), and generally have more documentation, testing, and community in place, besides being already proven and verified by others as an effective solution.
But all of this said, whether you choose to manually use fetch
, write your own wrapper, or use an open source wrapper - for the sake of your users and your team, please be sure to fetch your data properly :)
Hi! I'm Steve, CEO of Builder.io.
If you like our content, you can subscribe to us on dev.to, twitter, or our newsletter.
We make a way to drag + drop with your components to create pages and other CMS content on your site or app, visually.
You can read more about how this can improve your workflow here.
You may find it interesting or useful:
Builder.io is a Visual CMS that let's you drag and drop to create content on your site visually, using your components.
Stop drowning in a backlog of requests - build with your whole team instead.