When you are building the front-end parts of an application, you can be sure, that you’ll meet the task of using data from an API. Let it be the API of your backend or a 3rd party endpoint, the format that the data is going to arrive in (or must be sent) is usually different from source to source.
I would like to show you an approach that can make your life easier when dealing with various endpoints, by creating a “smooth internal surface” for handling API responses. By “smooth” I mean that internally you’ll only have to deal with one data structure, not with all the different responses that the APIs send back as a response.
During this article, I’m going to use a dummy API that you can find as https://jsonplaceholder.typicode.com. It’s a really nice service and can help us create the solution planned.
If you don’t want to follow the steps, then you can just scroll down to the end, and look at the result of this part. It’s a pretty solid base for fetching data with Vanilla JS.
Let’s start building up the first part with fetch:
Some problems with this: no chance of setting the endpoint from the outside, not handling error cases. Upgrade it, just a little:
One step nicer. But there’s one thing with using fetch as the tool: it doesn’t return any errors (not entirely the case, but mostly):
It returns a Promise that resolves to the Response to that request — as soon as the server responds with headers — even if the server response is an HTTP error status
(source: MDN Fetch API)
But how to handle the cases, when we get an error in the HTTP header? That means that the request was sent out, but for some reason, the endpoint sent an error message. The situation is not bad — we can handle that by using the .ok property of the fetch (more about this here):
If the response.ok is falsey, then we throw an object, thus switching to the catch part of our code (yes, throw and not throw new Error()) — eventually logging out the error. Right now the only property of the thrown object is isError, but it will get a bit “fatter” later.
Right now, the handler function only returns something, if the request was successful. And it returns a Promise. A bit simplified, but with REST endpoints this is true: we expect a JSON in the response (there could be other returned values, but JSON is a sensible simplification, I think). Let’s return to that:
Everything is fine, isn’t it? No. Nonononono.
This is a nice, simple handler, but we want a more robust one, that returns info even if the request returned with an error and also it’s easy to extend if we want to return more data. So,
- We need to return data if the request was unsuccessful
- We need to return data in an extensible format
- We need to return data ALWAYS in the same format — that’s the goal of this whole article :)
Do you see? I’ve told you that our return object would get a little “fatter”. We return (ok, throw) a value. But something is still missing around the errors: what if there’s a hard error, like “No network”? The request doesn’t even go out, so there’s no response back (not even !response.ok-type). We need to expand the catch block a bit, keeping in mind that we still want to return a value in a uniform structure.
That is one reason we used throw instead of throw new Error() (the other reason being the custom object defined): we can differentiate our custom return object from the errors.
OK, so what did we achieve with this setup?
- We covered the expected success case of a fetch
- We covered the error cases of a fetch
- We created a smooth return object that you can expect in your code when calling this function.
That’s an accomplishment! 🚀
But there’s still at least a couple of steps to go until it’s an easy-to-use API hub in our Vue application. I’ll update this article with the links to the later steps that discuss topics like how do we provide an enriched object from the API response to our app, or how could we incorporate it in stand-alone components that reflect the current state of the query.
Written with ❤️ & the intention to help.