Trying out something new: FastAPI

Tags: software-dev · Python · ml · how-i-fixed

I’m building an interactive demo to show off some deep learning image analysis at an open day later this year. I wanted it to be quite interactive and dynamic, so I’ve got a nice frontend UI with dynamically changing layouts and transition effects to engage the reader and keep focus on what I want. However, I was left with a puzzle: how to I get the analysis results from my backend to the frontend? My usual choice of rocket.rs (discussed previously here) presented some challenges.

Deep Learning and Python

My deep learning models are all implemented in PyTorch, because its ace. An image is passed to a model and results are returned. Internally, PyTorch passes the image data to the C++ API and runs it down at that level, before bringing things back up. I could deploy my models to C++ and run them directly, but I want to keep it simple and run in python. (I don’t really have the time to implement and test all that when I have a set of well-tested scripts ready to go.) Interoperability between python and other things is not the nicest thing in the world, but not impossible. To stay on the track of keeping it (stupidly) simple, I opted to check out a python web API library.

FastAPI

FastAPI seemed to fit the bill for me: high performance simple web framework for Python. Good stuff. Looking through their documentation and quick start, we can see starting an app with FastAPI is as simple as:

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def read_root():
    return "Hello, World!"

Hey! That seems pretty familiar… Compare with the example program rocket.rs gives:

#[macro_use] extern crate rocket;

#[get("/")]
fn index() -> &'static str {
    "Hello, world!"
}

#[launch]
fn rocket() -> _ {
    rocket::build().mount("/", routes![index])
}

Very similar indeed!

A problem

I got started used FastAPI: I had it serving files from a directory “site” on route “/”, and was expecting API calls on “/demo”.

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles

app = FastAPI()
app.mount("/", StaticFiles(directory="site", html=True))


@app.get("/demo")
def handle_query(command: str):
    match command:
        case "foo":
            ...
    ...

However, when I made the get request to the server no reply came! The request 404’d. Can you spot why?

What’s going on?

I had a think about it, and it seemed that the 404 (not found) error wouldn’t trigger if the request was bad (would be a 402, I think). 404 is because it can’t find a file. Aha! That’s the problem then, the rules are applied in order they were set up.

The solution

The solution was simple, swap the order of app.mount(...) and @app.get(...) so that the more exclusive rule for the demo API comes first, and the more general rule comes last.

Conclusions

I haven’t fully stress tested the server yet, but I already have noticed a marked speed-up over building the HTTP server myself using as I would have done previously. That small hiccup was resolved quickly and was my fault for not thinking when adding routing rules!

This post is a little shorter than I’d normally write, but wanted to put some quick notes down about FastAPI and the routing issue.


Questions? Comments? Get in touch on Twitter!