Packages Examples Agents Blog Get started
← Blog

Coming from FastAPI

FastAPI is a great HTTP layer. Oridecon keeps that instinct and puts a composition root around it — you are not starting over.

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, TestClientWebTestBed, app.state → the container, HTTPExceptionResult 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:

Terminal window
uv add oridecon-cli
oridecon new project my-app --template web-api
oridecon run

Walkthrough: Your First App.