The world’s leading publication for data science, AI, and ML professionals.

PicoAPI: FastAPI for microservices?

FastAPI is an amazing library, but we can improve life improvements when using it in a microservices architecture.

image by Zetong Li on unsplash
image by Zetong Li on unsplash

The microservices paradigm essentially involves a set of small discrete mini-applications working together as a whole larger application. This architecture enables smaller teams to support smaller parts of the application and clearly defines contracts between the different parts of an application. The most common way for microservices to communicate in this set up is via HTTP/RESTful API. When building applications as a microservice, it is common to use a service discovery and configuration application like Consul.

Very rarely do I come across a library and think, "wow, this is beautiful". The last time was the enigmatic Requests library for python. FastAPI’s typing and other modern python features make it an absolute dream for the developer and enables a rapid turnaround time from conceptualizing to production models. Not only does it enable fast development, the use of schemes for request and response being baked in mean that your API’s are quickly and easily debuggable and maintainable in the long run.

Image by Patrick Coffey (using draw.io)
Image by Patrick Coffey (using draw.io)

Rather than going all out and spinning up a copy of Consul, I often opt for a simpler approach. This involves configuring one of my microservices to act as the master and the others to register with the master as they spin up. This means that my stack only has to know the address of the master microservice, and the slaves can all send a PUT request to the master registering themselves, a set of tags defining the things they offer, and a health check URL that the master can GET on a schedule to test if that microservice is still reachable.

Above is the schema I use to represent a microservice registration with the master. The important things to note here are the tags field that I use to describe a microservice’s services. An example using something common to most of us data scientists; we have a web crawler module. It might have the tags: web_crawler, examplehost.com, dirty. This would indicate to the master that this microservice provides web crawling, it’s configured to scrape data from "examplehost.com", and that the output of this microservice is dirty (needs further cleaning for NLP tasks). These tags are obviously something that needs to be defined ahead of time, but I found this loose list of strings to be very flexible so far.

Fastapi (well, technically starlette) offers some hooks into its internal events. Some of these are the startup and shutdown events. These events allow us to add application logic that will cause the API to PUT its microservice definition to the master microservice, effectively registering it. Upon receiving one of these PUT requests, the Master node will place the service definition into its services directory and set up the described health check if once is defined.

PicoAPI can use a simple thread in python that can handle the Health Check information. Any response from within the 200–299 range of HTTP response codes will result in a healthy status. All other status codes will result in an unhealthy status. There may be some valid reasons why a microservice would return a 300 code, and this code may need to be changed. However, I haven’t encountered these situations yet in my use of this architecture. As long as the master microservice keeps a reference to the instantiated object, it can simply check the HealthCheck.healthy variable to see if that health service was running on the last check. The only other piece of logic that needs to be implemented here is a health check endpoint on each slave that returns a 200 code when the microservice is correctly running.

Image by Patrick Coffey (using draw.io)
Image by Patrick Coffey (using draw.io)

What extra endpoints might a master require? Since the master has taken the responsibility of handling service registrations, it needs an endpoint that would allow other slaves to check which services have registered. In this regard, it acts as an address book of registered services.

Image by Patrick Coffey (using draw.io)
Image by Patrick Coffey (using draw.io)

I also chose to return not just the registered services but their OpenAPI service definitions. This allows slaves to discover extra context about other registered slaves. While I haven’t personally needed this level of introspection between my microservices, it is effortless to implement, so I included it.


The code from this post has been wrapped up into a python library and published on PyPI for anyone who might want to make use of it. The library is still in its infancy, but I am successfully using it in a few personal projects. Please submit any issues, suggestions and PR’s on the GitHub repo so I can deal with them properly.

You can find it here on GitHub:

schlerp/picoapi

and here on PyPI:

picoapi


Related Articles