Side by side
| Topic | FastAPI | Austial |
|---|---|---|
| Architecture | Unopinionated, you choose the layout | Modules own controllers and providers, one folder per feature |
| Dependency injection | Depends() wired at each route | Constructor injection resolved from type hints |
| Request lifecycle | Middleware and dependencies you compose | Guards, pipes, interceptors and exception filters in a fixed order |
| Routing | Decorators on an app or router | @Controller with @Get and @Post, and :id path parameters |
| Error shape | HTTPException and 422 validation errors | One consistent JSON error shape through exception filters |
| Database | Bring your own library | austial.orm with repositories and Alembic migrations |
| Project setup | Your own scaffolding | austial new, generate and serve |
| Testing | TestClient or httpx against your app | A testing module with a real DI graph |
When Austial is a good fit
- A backend that will grow across many features and several contributors.
- Teams that want one agreed way to structure modules, services and cross-cutting concerns.
- Services that need authentication, tenancy, security defaults and background jobs from day one.
- Developers coming from NestJS who want the same structure in Python.
When plain FastAPI is enough
A small service, a prototype, a script with a few endpoints or a single-file API does not need modules and a request pipeline. If you can hold the whole codebase in your head, FastAPI alone keeps things lighter.
The trade-offs
Austial is opinionated, and it is not FastAPI, Django or NestJS, so you learn its API instead of assuming one. A few concepts are out of scope for now: request-scoped providers, enforced module visibility and RxJS-style interceptor streams.
The same endpoint, both ways
Here is a read endpoint that needs a service. In FastAPI the dependency is wired at the route with Depends(). In Austial the controller declares what it needs in its constructor, and the module registers the provider once.
# FastAPI
from fastapi import Depends, FastAPI
app = FastAPI()
def get_cats_service() -> CatsService:
return CatsService()
@app.get("/cats/{id}")
async def find_one(id: int, service: CatsService = Depends(get_cats_service)) -> Cat:
return service.find_one(id)The Austial version
# Austial
from austial import Controller, Get, Module
@Controller("cats")
class CatsController:
def __init__(self, service: CatsService):
self.service = service
@Get(":id")
async def find_one(self, id: int) -> Cat:
return self.service.find_one(id)
@Module(controllers=[CatsController], providers=[CatsService])
class CatsModule:
passFrequently asked questions
Is Austial a replacement for FastAPI?
No. Austial is built on FastAPI. Your routes are real FastAPI routers and your request bodies are Pydantic models.
Will my FastAPI knowledge transfer?
Yes. Pydantic, type hints, async handlers and the OpenAPI docs work as you expect. What changes is how you organise code and wire dependencies.
Can I still see Swagger UI?
Yes. Scaffolded projects serve FastAPI's Swagger UI at /docs.