If you already write FastAPI, you already know most of the HTTP layer Oridecon uses. Starlette routing. Pydantic request shapes. OpenAPI. TestClient-shaped tests. That is not an accident — both sit on Starlette.
This is not a replacement pitch. It is a map of what stays in your hands, and what moves one level up so the rest of the application — SQL, cache, queues, auth, AI — feels as designed as the routes.
What you keep
A path operation still looks like a path operation:
from oridecon.web import Controller, get
class UserController(Controller): prefix = "/users"
@get("/{user_id}") async def get_user(self, user_id: str) -> dict: return {"id": user_id, "name": "Ada"}{user_id} is the same Starlette syntax. Pydantic models still work for bodies. OpenAPI still falls out of the web layer. If you have been writing FastAPI for years, this page should feel like a dialect, not a new language.
What moves up one level
FastAPI’s Depends() is a good idea: declare what the handler needs, get it resolved. Oridecon does the same work on the constructor, so a service is equally easy to call from a route, a task, or a test.
class OrderController(Controller): prefix = "/orders"
def __init__(self, repo: OrderRepository) -> None: self.repo = repo
@get("/") async def list_orders(self) -> list[dict]: return await self.repo.find_all()Startup and shutdown hooks still exist as a pattern. Oridecon names them register(), boot(), and shutdown(), ordered by ProviderPriority, so the database is connected before the first request and torn down in reverse.
Expected HTTP failures still become status codes. In the domain they are Result values, so the same UserService.find() works behind a queue or a CLI without raising HTTPException into the wrong layer.
The rest of the app
The reason to add a composition root is not the first route. It is the fifth backend.
FastAPI does not stop you from importing SQLAlchemy in a handler. That is a reasonable way to start. Oridecon’s bet is that when SQL, cache, and an LLM client all show up, they should talk through oridecon-contracts — so swapping Redis for in-memory in tests, or Postgres for a replica, is config, not a rewrite of callers.
You install what you need. oridecon-web is the HTTP layer. oridecon-sql, oridecon-auth, oridecon-ai-llm are separate packages. They never import each other.
You do not have to rewrite overnight
A single new service, or a single new endpoint, is a valid first step. Keep the FastAPI app you already have. Port one bounded context when you want the container, the providers, and the contracts in the same place as the routes.
The migration guide is the concept map: Depends() → constructor, TestClient → WebTestBed, app.state → the container, HTTPException → Result at the domain and HTTP at the edge.
If Laravel’s DX is how the rest of the stack is shaped, that story is here. If you just want a compiling app:
uv add oridecon-clioridecon new project my-app --template web-apioridecon runWalkthrough: Your First App.