FastAPI, Litestar, Nameko and Django compared for Python services that call each other and share events, plus how Encore adds the databases, topics and cloud resources underneath whichever one you choose.
Python teams building microservices rarely pick a framework designed for them, since the popular choices are general web frameworks and the service boundaries come from how the team splits the code. FastAPI has become the default for new services, and the JetBrains Python Developers Survey 2024 shows its usage among respondents rising from 21 percent in 2021 to 38 percent in 2024, ahead of Django at 35 percent and Flask at 34 percent.
The framework decides how one service handles requests and leaves open how services find each other, how events move between them, and where each service's database comes from.
What to weigh when choosing:
FastAPI builds on Starlette and Pydantic, so a service's request and response models are Python classes and the OpenAPI schema other teams consume is generated from them.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Order(BaseModel):
user_id: int
@app.post("/orders", status_code=201)
async def place_order(order: Order) -> dict:
return {"user_id": order.user_id}
Depends.FastAPI is a single-service framework, so service URLs, the broker and each database are configured by the team, usually through environment variables per environment.
Litestar has a similar async, type-driven model with more in the box, including layered dependency injection, DTOs for shaping responses, and plugins for SQLAlchemy and Pydantic.
Litestar has a smaller community than FastAPI, so there are fewer third-party examples, and like FastAPI it leaves inter-service communication to you.
NamekoNameko is one of the few Python frameworks built specifically for microservices, with RPC calls and event dispatch between services carried over RabbitMQ.
from nameko.events import EventDispatcher
from nameko.rpc import rpc
class OrdersService:
name = "orders"
dispatch = EventDispatcher()
@rpc
def place_order(self, user_id):
self.dispatch("order_placed", {"user_id": user_id})
return {"user_id": user_id}
@rpc methods are callable from other Nameko services by name.Nameko depends on eventlet and RabbitMQ, its 3.0 line has been in release candidates since 2018, with the latest, 3.0.0rc11, published to PyPI in December 2021, and it predates the asyncio ecosystem most Python libraries target now, so check how well its dependencies fit your stack before adopting it.
Django brings an ORM, migrations and an admin, which suits a service that owns a complex model and needs internal tooling around it.
Each Django service carries a full project layout and settings module, which is more overhead per service than FastAPI, and async support covers views but not every part of the ORM.
Encore combines each Python service with its own infrastructure through an infrastructure SDK that you install into the service, so FastAPI or Django keeps handling requests while each service declares its own Postgres database, Pub/Sub topics, buckets, cron jobs and secrets as module-level objects. Encore reads those declarations and the calls between services from the code, encore run starts Postgres locally with the topics and buckets next to the services, and a deploy provisions the same resources in your own AWS or GCP account. Whichever option above you pick, Encore can add the databases, topics and buckets to the same services.
An orders service built on FastAPI declares its database and topic in one module:
# orders/resources.py
from encore import pubsub
from encore.storage import postgres
from pydantic import BaseModel
db = postgres.Database("orders", migrations="./migrations")
class OrderPlaced(BaseModel):
order_id: int
user_id: int
order_placed = pubsub.Topic[OrderPlaced]("order-placed")
The endpoint calls the users service through the URL Encore resolves for it, writes the order and publishes the event:
# orders/main.py
from contextlib import asynccontextmanager
import encore
import httpx
from encore.services import base_url
from fastapi import FastAPI
from resources import OrderPlaced, db, order_placed
@asynccontextmanager
async def lifespan(app: FastAPI):
await encore.start()
yield
app = FastAPI(lifespan=lifespan)
@app.post("/orders", status_code=201)
async def place_order(user_id: int) -> dict:
async with httpx.AsyncClient() as client:
resp = await client.get(f"{base_url('users')}/users/{user_id}")
resp.raise_for_status()
row = await db.query(
"INSERT INTO orders (user_id) VALUES ($1) RETURNING id", user_id
).one()
await order_placed.publish(OrderPlaced(order_id=row["id"], user_id=user_id))
return {"order_id": row["id"]}
A notifications service subscribes to the topic by name, without importing the orders code:
# notifications/resources.py
from encore import pubsub
from pydantic import BaseModel
class OrderPlaced(BaseModel):
order_id: int
user_id: int
order_placed = pubsub.Topic[OrderPlaced].named("order-placed")
async def send_receipt(message: pubsub.Message) -> None:
print("Sending receipt for order", message.data.order_id)
receipts = pubsub.Subscription[OrderPlaced](
order_placed, "send-receipt", handler=send_receipt, max_retries=3
)
base_url are read from the code, which gives Encore a map of which service depends on which.Encore leaves request handling to your framework, so the comparison below still decides how each service is written. When Encore deploys to your cloud, the cloud access sits with Encore's IAM role, not the developer or the agent writing the service. Python support is in beta, and you can sign up for access.
Each service's contract is easier to share when it is published as an OpenAPI document, which FastAPI and Litestar generate from the code, and the database per service pattern keeps each service's data private to it. Pick the framework for how you want each service written, and plan the infrastructure separately.
Every service also needs its own environment for testing changes, and Encore's preview environments give each pull request one with real infrastructure on paid plans. Python support in Encore is in beta, and you can sign up for access.
FastAPI is the most common choice for new Python services because it is async, small and generates OpenAPI schemas from type hints. Litestar offers a similar model with more built in, Nameko is built around RPC and events between services, and Django suits services that need an ORM and admin. Encore is not a competing framework but an infrastructure SDK you add to the framework you pick, giving each service its own Postgres database and Pub/Sub topics.
Django works for services with a rich data model and an admin, but its project structure and synchronous history make it heavier per service than FastAPI or Litestar. Many teams run Django as one larger service next to smaller FastAPI services.
Look up the other service with encore.services.base_url, passing the service name as a string literal, and send an HTTP request to the returned URL with httpx. Encore reads the call from the code, records the dependency between the two services, and resolves the URL locally under encore run and in each deployed environment.
Usually not. Most teams start with one service and split when a part of the system needs to scale or deploy on its own. Keeping each module behind its own database and API from the start makes that split cheaper later.
Deploy to your cloud on AWS and Google Cloud
without managing infrastructure
Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.