Search code examples
pythonreverse-proxyfastapiaws-application-load-balancer

Root Path in Fast API behind ALB


I am deploying a fast API app behind AWS ALB, with listener rule path pattern /api/v1/ points towards fast API. My app looks like this

from typing import Union
import os
import mysql.connector
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def read_root():
    print("Root path hit")
    return {"App": "Fargate"}


@app.get("/api/v1/")
def read_apiv1():
    print("Root path hit")
    return {"App": "Path Fargate API v1"}

I deployed the app in ECS using docker and my docker run command is

CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80", "--root-path", "/api/v1"]

Now when I hit my AWS ALB dns suffixed with /api/v1/ I see the endpoint /api/v1 which throws the response {"App": "Path Fargate API v1"}. However, based on the documentation from fast API it should load the api endpoint with /.

Can anyone help me why I am getting this unexpected behavior? Do I have to manually write /api/v1 before all of my endpoints?


Solution

  • Having a proxy with a stripped path prefix, in this case, means that you could declare a path at /app in your code, but then, you add a layer on top (the proxy) that would put your FastAPI application under a path like /api/v1.

    In this case, the original path / would actually be served at /api/v1.

    Even though all your code is written assuming there's just /.

    And the proxy would be "stripping" the path prefix on the fly before transmitting the request to Uvicorn, keep your application convinced that it is serving at /, so that you don't have to update all your code to include the prefix /api/v1.

    Reference: https://fastapi.tiangolo.com/advanced/behind-a-proxy/?h=root_#proxy-with-a-stripped-path-prefix

    About root_path

    Have in mind that the server (Uvicorn) won't use that root_path for anything else than passing it to the app.

    But if you go with your browser to http://127.0.0.1:8000/app you will see the normal response:

    {
        "message": "Hello World",
        "root_path": "/api/v1"
    }
    

    So, it won't expect to be accessed at http://127.0.0.1:8000/api/v1/app.

    Uvicorn will expect the proxy to access Uvicorn at http://127.0.0.1:8000/app, and then it would be the proxy's responsibility to add the extra /api/v1 prefix on top.