You will build a two-endpoint API and see dependency injection resolve a provider without registering it anywhere.
You need Python 3.12+ and Bustan 0.0.2.
Install#
pip install bustan==0.0.2
A provider#
# app/tasks/service.py
from bustan import Injectable
@Injectable()
class TaskService:
def __init__(self) -> None:
self._tasks: dict[int, str] = {}
self._next_id = 1
def add(self, title: str) -> int:
task_id = self._next_id
self._tasks[task_id] = title
self._next_id += 1
return task_id
def all(self) -> dict[int, str]:
return dict(self._tasks)
A controller#
Since 0.0.2, request bodies are declared as Pydantic models rather than loose
Body() parameters:
# app/tasks/controller.py
from pydantic import BaseModel
from bustan import Controller, Get, Post
from .service import TaskService
class CreateTask(BaseModel):
title: str
@Controller("/tasks")
class TaskController:
def __init__(self, tasks: TaskService) -> None:
self.tasks = tasks
@Get("/")
async def list_tasks(self) -> dict[int, str]:
return self.tasks.all()
@Post("/")
async def create(self, payload: CreateTask) -> dict[str, int]:
return {"id": self.tasks.add(payload.title)}
A malformed body now fails validation before your handler runs, and the error shape is consistent across the app.
A module#
# app/tasks/module.py
from bustan import Module
from .controller import TaskController
from .service import TaskService
@Module(controllers=[TaskController], providers=[TaskService])
class TaskModule:
pass
# app/main.py
from bustan import Module, Bustan
from .tasks.module import TaskModule
@Module(imports=[TaskModule])
class AppModule:
pass
app = Bustan(AppModule)
Run it#
uvicorn app.main:app --reload
$ curl -s -X POST localhost:8000/tasks/ -d '{"title":"write docs"}' -H 'content-type: application/json'
{"id":1}
$ curl -s -X POST localhost:8000/tasks/ -d '{}' -H 'content-type: application/json'
{"error":"validation_failed","detail":[{"field":"title","message":"field required"}]}
Upgrading from 0.0.1#
| 0.0.1 | 0.0.2 |
|---|---|
title: str = Body() |
a Pydantic model parameter |
| — | guards, interceptors and pipes |
| — | bustan test utilities |
Bustan(AppModule) |
unchanged |
Body() still works and warns. It is removed in 0.1.0.
Next#
- Write tests for this — see the next tutorial
- Add a guard to protect a route — see the how-to guides