I am a teacher, I teach coding to kids. I am trying to learn backend development.
In my journey to learn FastAPI I got to make many APIs. But I'd never gotten a chance to use an external API. So I built a service that lives entirely on other people's APIs- weather, currency and news. Here's what it taught me.
Chaining API calls-
I used Open-meteo's weather API, which requires the latitudes and longitude of the city whose weather we want to know. I had to use another API of open-meteo to get the latitudes and longitude of any city in the world by using its name. So, we get the latitudes and longitude of city whose weather is requested by the client, from the first API and give that as input to the weather API. The output of one API became the input of the next.
Whose failure is it-
I handled the failures of the system in a way that the client knows whose fault it is. If the request fails because of the timeout error then the client should be informed about it so they don't keep trying, if our own server fails, then client can inform us. A 500 error if our own server fails, 502 error if the external API provided an invalid response and 504 error if the external API service timed out.
I tested this by setting my timeout to 0.001 seconds, forcing every call to fail. Before: an ugly 500 traceback. After: a clean 504 weather service timed out.
Graceful degradation-
I made an aggregate endpoint which gets the responses from all three APIs and gives it to the client. Client enters the city name and we get the weather of the city, currency conversion and news. If any of these 3 APIs fail, we only get the responses from the successful APIs and also the name of the API that's causing the error and give it to the user.
In case user enters an unknown city name, the request would be killed and client would get a 404 error. I could have followed the same way of handling the unknown city failure just like the ones we mentioned before but it just didn't make sense to me to give the currency conversion and news if the city is not recognised.
Caching-
I used a TTL cache for my project, every response is remembered for 5 minutes which saves a lot of resources. But, TTL cache has its own limits, one of which is that if the system is to be stopped and restarted for some reason we might get a lot of requests from the clients which is called cache stampede.
Another big issue with TTL is that it lives in the process, if any other workers in the project want to use it, it wouldn't work.
Testing with mocks-
I set up tests with Pytest and tested all the endpoints, including the failure paths.. The project contains the code for what would happen if the external APIs timed out or gave an invalid response. That code was untestable because we can't actually wait for the API to be down to test that code.
So I staged the disasters myself, during tests, I replaced httpx with a fake that returns whatever I script — a 500, a timeout. My real error-handling code runs against fake failures, and the tests verify it responds correctly, without any real service being down.
I learned not only how to use external APIs but also pytest in this project. Here's the link for the GitHub repo:
https://github.com/Gunjan-redu/api-aggregator
Top comments (0)