APIs for AIFastAPI 0.141 · httpx2 2.13 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
19 small wins to finish your pathNext lesson

Depends: protecting and swapping parts of a service

Anyone who can reach the service can spend your model budget. A dependency that checks a key fixes that, and the same tool swaps the model client in tests.

Exampleservice.py, new
def require_service_key(x_api_key: Annotated[str | None, Header()] = None):
    if x_api_key != os.environ.get("SERVICE_KEY", "team-key-1"):
        raise HTTPException(status_code=401, detail="send your team key in X-API-Key")
Exampleservice.py, the decorator now
@app.post("/triage", dependencies=[Depends(require_service_key)])

require_service_key reads an X-API-Key header. Listed in dependencies=, it runs before the endpoint on every request, and its HTTPException stops the request before the model is called. The X- prefix is the usual start for a header an application makes up.

Swapping the model client

To call the service from Python without starting the model API, replace get_model_client with a function that returns a test client for model_api.app:

Example
from fastapi.testclient import TestClient

import model_api
import service

def fake_model_client():
    return TestClient(model_api.app, headers={"Authorization": "Bearer sk-local-123"})

service.app.dependency_overrides[service.get_model_client] = fake_model_client
client = TestClient(service.app)

ticket = {"id": 2, "text": "My parcel has not arrived"}
print(client.post("/triage", json=ticket).status_code)
print(client.post("/triage", json=ticket, headers={"X-API-Key": "team-key-1"}).json())

dependency_overrides is a dictionary on the app: whenever an endpoint depends on the key, FastAPI calls the value instead. The endpoint code does not change, and it cannot tell.

The first request had no service key and stopped at 401. The second passed, called the stand-in in memory, and sorted the ticket. The model API's own key and the service's key are separate: one says the service may call the model, the other says a client may call the service.

Why not one key
If clients sent the model API key itself, every client could call the model directly and you could not turn one client off without changing the key for all of them.
Try it yourself
  • Send X-API-Key: wrong and print the body.
  • Set SERVICE_KEY in the environment before starting Python, and send that value.
  • Make fake_model_client return a client with a wrong model key. What status does the service send back?

Little by little, you're building something great.