FastAPI Microservices for AI Applications: A Practical Guide
By Asadullah Khan · AI & Voice AI Engineer, Karachi, Pakistan
12 October 2026 · 7 min read
AI features live or die by the backend around them. A model that answers in 200 milliseconds is not useful if the surrounding API times out or blocks under load. These are the FastAPI patterns I reach for when building services that wrap LLMs, embeddings and real-time pipelines.
Async everywhere the I/O is remote
Model calls, vector searches and database queries are almost entirely waiting. Async endpoints let a single worker handle many concurrent requests instead of tying up a thread per call.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class SummarizeRequest(BaseModel):
text: str
@app.post("/summarize")
async def summarize(payload: SummarizeRequest) -> dict:
summary = await llm_client.summarize_async(payload.text)
return {"summary": summary}Push slow work to background tasks
Indexing a document, sending a webhook or warming a cache should not block a user-facing request. Hand that work to a task queue and return immediately with a job identifier.
from fastapi import BackgroundTasks
@app.post("/documents", status_code=202)
async def ingest(text: str, tasks: BackgroundTasks) -> dict:
document_id = await save_document(text)
tasks.add_task(index_document, doc_id=doc_id)
return {"id": doc_id, "status": "processing"}Dependency injection keeps services testable
Define your database session, current user and model client as dependencies. Endpoints then receive exactly what they need, and tests can swap in fakes without touching the route logic.
from fastapi import Depends, HTTPException
async def get_current_user(token: str = Depends(oauth2_scheme)):
user = await verify_token(token)
if user is None:
raise HTTPException(status_code=401, detail="Invalid token")
return user
@app.get("/me")
async def read_me(user=Depends(get_current_user)):
return userHealth checks that mean something
A health endpoint should verify its dependencies, not just return 200. Separate liveness from readiness so a struggling vector store drains traffic instead of failing requests.
- Liveness: the process is running and can answer.
- Readiness: the database, cache and model gateway are reachable.
- Prefer bounded timeouts on every dependency check.
Observability and failure handling
AI backends fail in ways ordinary APIs do not: rate limits, partial streams and slow model responses. Structure code so these are expected states rather than crashes.
- Propagate a request ID through logs and into model calls.
- Retry idempotent model calls with exponential backoff and jitter.
- Set explicit timeouts on every outbound call.
- Emit latency histograms per stage so you can see where time goes.
Where this leads
Get these five things right — async I/O, background work, clean dependencies, honest health checks and real observability — and an AI backend starts to behave like ordinary, dependable software. That reliability is what lets a model feature ship to real users.