AI Agent Backend Platform on FastAPI.
MCP server + AI orchestration + async DDD โ from CRUD to agent tools in one codebase.
Build AI-powered backends that serve both users and agents.
Quick Start ยท Architecture ยท AI-Native Development ยท Comparison ยท ํ๊ตญ์ด
- MCP Server interface
plannedโ Expose domain services as AI agent tools via FastMCP - AI agent orchestration
plannedโ PydanticAI integration for structured LLM workflows - Vector search
plannedโ pgvector for semantic search, RAG, and similarity matching
- 4 interface types โ HTTP API (FastAPI) + Async Worker (Taskiq) + Admin UI (SQLAdmin) + MCP Server (planned)
- Zero-boilerplate CRUD โ Inherit
BaseRepository[DTO]+BaseService[DTO], get 7 async CRUD methods instantly - Auto domain discovery โ Add a domain folder, it auto-registers. No container or bootstrap changes needed
- Async-first โ Genuine async from DB (asyncpg) to HTTP (aiohttp) to task queue (Taskiq)
- 13 AI development skills โ Claude Code slash commands for scaffolding, testing, architecture review, and more
- Architecture enforcement โ Pre-commit hooks block
Domain -> Infrastructureimports at commit time - Type-safe generics โ
BaseRepository[ProductDTO],BaseService[ProductDTO],SuccessResponse[ProductResponse] - DDD layered structure โ Each domain is fully independent with its own layers (Domain / Infrastructure / Interface / Application)
- 14 Architecture Decision Records โ Every major design choice documented with rationale
Write business logic once. Expose it as a REST API, background job, admin view, or AI agent tool.
# 1. Define your service
class DocumentService(BaseService[DocumentDTO]):
async def analyze(self, document_id: int) -> AnalysisDTO:
... # your business logic
# 2. REST API โ for your frontend
@router.post("/documents/{document_id}/analyze")
async def analyze_document(document_id: int, service=Depends(...)):
return await service.analyze(document_id)
# 3. MCP Tool โ for AI agents (planned)
@mcp.tool()
async def analyze_document(document_id: int) -> AnalysisResult:
return await document_service.analyze(document_id)
# 4. Background job โ for batch processing
@broker.task()
async def batch_analyze(project_id: int):
for doc in await service.get_by_project(project_id):
await service.analyze(doc.id)# Before: Repeat the same CRUD for every domain
@router.post("/user")
async def create_user(user: UserCreate):
db = get_db()
new_user = User(**user.dict())
db.add(new_user)
db.commit()
return new_user
@router.post("/product") # Repeat again...
async def create_product(product: ProductCreate):
db = get_db()
new_product = Product(**product.dict())
db.add(new_product)
db.commit()
return new_product# After: One inheritance line, CRUD done
class ProductRepository(BaseRepository[ProductDTO]):
def __init__(self, database: Database):
super().__init__(database=database, model=ProductModel, return_entity=ProductDTO)
class ProductService(BaseService[ProductDTO]):
def __init__(self, product_repository: ProductRepositoryProtocol):
super().__init__(repository=product_repository)
# 7 CRUD methods provided automatically -- just add your custom logicRouter -> Service(BaseService) -> Repository(BaseRepository) -> DB
^ Simple CRUD: this is all you need
Router -> UseCase -> Service -> Repository -> DB
^ Add only when complex business logic is required
| Layer | Role | Base Class |
|---|---|---|
| Interface | Router, Request/Response, Admin, Worker Task, MCP Tool | - |
| Domain | Service (business logic), Protocol, DTO, Event | BaseService[ReturnDTO] |
| Infrastructure | Repository (DB access), Model, DI Container | BaseRepository[ReturnDTO] |
| Application | UseCase (orchestration) -- optional | - |
Write: Request --> Service --> Repository --> Model -> DB
Read: Response <-- Service <-- Repository <-- DTO <-- Model
- Request is passed directly to Service (no extra conversion needed)
- Repository handles Model -> DTO conversion (
model_validate(model, from_attributes=True)) - Router handles DTO -> Response conversion (
Response(**dto.model_dump(exclude={...})))
| Object | Role | Location |
|---|---|---|
| Request/Response | API contract | interface/server/schemas/ |
| DTO | Internal data transfer between layers | domain/dtos/ |
| Model | DB table mapping (never exposed outside Repository) | infrastructure/database/models/ |
This template works great on its own. With Claude Code, it's magic.
Complex architecture? Type /onboard -- it explains everything at your level.
The /onboard skill adapts to your experience and learning style:
- Beginner / Intermediate / Advanced -- depth adjusts to your level
- Guided -- structured Phase-by-Phase walkthrough
- Q&A -- topic maps provided, explore by asking questions
- Explore -- point at any code freely, uncovered essentials flagged at the end
| Command | What it does |
|---|---|
/onboard |
Interactive onboarding -- adapts to your experience level and learning style |
/new-domain {name} |
Scaffold an entire domain (21+ source files + tests) |
/add-api {description} |
Add API endpoint to existing domain |
/add-worker-task {domain} {task} |
Add async Taskiq background task |
/add-cross-domain from:{a} to:{b} |
Wire cross-domain dependency via Protocol DIP |
/plan-feature {description} |
Requirements interview -> architecture -> security -> task breakdown |
/review-architecture {domain} |
Architecture compliance audit (20+ checks) |
/security-review {domain} |
OWASP-based security audit |
/test-domain {domain} |
Generate or run domain tests |
/fix-bug {description} |
Structured bug fixing workflow |
/review-pr {number} |
Architecture-aware PR review |
/sync-guidelines |
Sync docs after design changes |
/migrate-domain {command} |
Alembic migration management |
Install the pyright-lsp plugin for code intelligence (symbol navigation, references, diagnostics):
uv sync # installs pyright binary as dev dependency
claude plugin install pyright-lsp # installs Claude Code plugin
enabledPluginsin.claude/settings.jsonwill prompt installation automatically on first run.
context7 -- Up-to-date library documentation
{
"mcpServers": {
"context7": {
"command": "npx",
"args": ["-y", "@upstash/context7-mcp@latest"]
}
}
}The project works without MCP servers. AIDD skills require MCP server configuration.
# 1. Clone
git clone https://github.com/Mr-DooSun/fastapi-agent-blueprint.git
cd fastapi-agent-blueprint
# 2. Setup (requires uv)
make setup
# 3. Set up environment variables
cp _env/local.env.example _env/local.env
# 4. Start PostgreSQL + run migrations + start server
make devOpen http://localhost:8000/docs-swagger to explore the API.
Manual setup (without Make)
# 2. Create virtual environment + install dependencies
uv venv --python 3.12
source .venv/bin/activate
uv sync --group dev
# 3. Set up environment variables
cp _env/local.env.example _env/local.env
# 4. Start PostgreSQL (Docker)
docker compose -f docker-compose.local.yml up -d postgres
# 5. Run migrations + start server
alembic upgrade head
python run_server_local.py --env localUsing Product domain as an example:
# src/product/domain/dtos/product_dto.py
class ProductDTO(BaseModel):
id: int = Field(..., description="Product ID")
name: str = Field(..., description="Product name")
price: int = Field(..., description="Price")
created_at: datetime
updated_at: datetime
# src/product/domain/protocols/product_repository_protocol.py
class ProductRepositoryProtocol(BaseRepositoryProtocol[ProductDTO]):
pass
# src/product/domain/services/product_service.py
class ProductService(BaseService[ProductDTO]):
def __init__(self, product_repository: ProductRepositoryProtocol):
super().__init__(repository=product_repository)
# CRUD provided automatically. Just add custom logic.# src/product/infrastructure/database/models/product_model.py
class ProductModel(Base):
__tablename__ = "product"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(255), nullable=False)
price: Mapped[int] = mapped_column(Integer, nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
updated_at: Mapped[datetime] = mapped_column(DateTime, onupdate=func.now())
# src/product/infrastructure/repositories/product_repository.py
class ProductRepository(BaseRepository[ProductDTO]):
def __init__(self, database: Database):
super().__init__(database=database, model=ProductModel, return_entity=ProductDTO)
# src/product/infrastructure/di/product_container.py
class ProductContainer(containers.DeclarativeContainer):
core_container = providers.DependenciesContainer()
product_repository = providers.Singleton(ProductRepository, database=core_container.database)
product_service = providers.Factory(ProductService, product_repository=product_repository)# src/product/interface/server/routers/product_router.py
@router.post("/product", response_model=SuccessResponse[ProductResponse])
@inject
async def create_product(
item: CreateProductRequest,
product_service: ProductService = Depends(Provide[ProductContainer.product_service]),
) -> SuccessResponse[ProductResponse]:
data = await product_service.create_data(entity=item)
return SuccessResponse(data=ProductResponse(**data.model_dump()))discover_domains() automatically detects new domains.
No changes needed in _apps/ containers or bootstrap files.
Discovery conditions:
src/{name}/__init__.pyexistssrc/{name}/infrastructure/di/{name}_container.pyexists
With Claude Code, just run
/new-domain productto scaffold all of this automatically.
Each domain can expose functionality through multiple interfaces:
| Interface | Technology | Status | Purpose |
|---|---|---|---|
| HTTP API | FastAPI | Stable | REST API endpoints |
| Async Worker | Taskiq + SQS | Stable | Background task processing |
| Admin UI | SQLAdmin | Stable | Database management dashboard |
| MCP Server | FastMCP | Planned | AI agent tool interface |
All interfaces share the same Domain and Infrastructure layers -- write your business logic once, expose it everywhere.
| Technology | Purpose |
|---|---|
| FastMCP | MCP server โ expose domain services as tools for AI agents |
| PydanticAI | Structured LLM orchestration with Pydantic-native outputs |
| pgvector | Vector similarity search (PostgreSQL extension) |
| Technology | Purpose |
|---|---|
| FastAPI | Async web framework |
| Pydantic 2.x | Data validation & settings |
| SQLAlchemy 2.0 | Async ORM |
| dependency-injector | IoC Container (why?) |
| Technology | Purpose |
|---|---|
| PostgreSQL + asyncpg | Primary RDBMS |
| Taskiq + AWS SQS | Async task queue (why not Celery?) |
| aiohttp | Async HTTP client |
| aioboto3 | S3/MinIO storage |
| Alembic | DB migrations |
| Technology | Purpose |
|---|---|
| Ruff | Linting + formatting (replaces 6 tools) |
| pre-commit | Git hook automation + architecture enforcement |
| UV | Python package management (why not Poetry?) |
| SQLAdmin | DB admin UI |
src/
โโโ _apps/ # App entry points
โ โโโ server/ # FastAPI HTTP server
โ โโโ worker/ # Taskiq async worker
โ โโโ admin/ # SQLAdmin dashboard
โ
โโโ _core/ # Shared infrastructure
โ โโโ domain/
โ โ โโโ protocols/ # BaseRepositoryProtocol[ReturnDTO]
โ โ โโโ services/ # BaseService[ReturnDTO]
โ โโโ infrastructure/
โ โ โโโ database/ # Database, BaseRepository[ReturnDTO]
โ โ โโโ http/ # HttpClient, BaseHttpGateway
โ โ โโโ taskiq/ # SQS Broker, TaskiqManager
โ โ โโโ storage/ # S3/MinIO
โ โ โโโ di/ # CoreContainer
โ โ โโโ discovery.py # Auto domain discovery
โ โโโ application/dtos/ # BaseRequest, BaseResponse, SuccessResponse
โ โโโ exceptions/ # Exception handlers, BaseCustomException
โ โโโ config.py # Settings (pydantic-settings)
โ
โโโ user/ # Example domain
โ โโโ domain/
โ โ โโโ dtos/ # UserDTO
โ โ โโโ protocols/ # UserRepositoryProtocol
โ โ โโโ services/ # UserService(BaseService[UserDTO])
โ โ โโโ exceptions/ # UserNotFoundException
โ โ โโโ events/ # UserCreated, UserUpdated
โ โโโ infrastructure/
โ โ โโโ database/models/ # UserModel
โ โ โโโ repositories/ # UserRepository(BaseRepository[UserDTO])
โ โ โโโ di/ # UserContainer
โ โโโ interface/
โ โโโ server/ # routers/, schemas/, bootstrap/
โ โโโ worker/ # tasks/, bootstrap/
โ โโโ admin/ # SQLAdmin views
โ
โโโ migrations/ # Alembic
โโโ _env/ # Environment variables
โโโ docs/history/ # Architecture Decision Records
| Feature | FastAPI Agent Blueprint | tiangolo/full-stack | s3rius/template | teamhide/boilerplate |
|---|---|---|---|---|
| MCP Server interface | Planned | No | No | No |
| AI orchestration (PydanticAI) | Planned | No | No | No |
| Vector search (pgvector) | Planned | No | No | No |
| Zero-boilerplate CRUD (7 methods) | Yes | No | No | No |
| Auto domain discovery | Yes | No | No | No |
| Architecture enforcement (pre-commit) | Yes | No | No | No |
| AI development skills | 13 | 0 | 0 | 0 |
Adaptive onboarding (/onboard) |
Yes | No | No | No |
| Multi-interface (API+Worker+Admin+MCP) | 4 types | 2 | 1 | 1 |
| Architecture Decision Records | 14 | 0 | 0 | 0 |
| Type-safe generics across layers | Yes | Partial | Partial | No |
| DI with IoC Container | Yes | No | No | No |
Every technical choice in this project is documented as an ADR (Architecture Decision Record).
| # | Title |
|---|---|
| 004 | DTO/Entity responsibility redefined |
| 006 | Domain-driven layered architecture |
| 007 | DI container hierarchy and app separation |
| 011 | 3-Tier hybrid architecture |
| 012 | Ruff adoption |
| 013 | Why IoC Container over inheritance |
- Structured logging โ structlog (#9)
- Per-environment config (#7, #8, #16)
- Error notifications (#17)
- Worker payload schemas (#37)
- CRUD data validation (#10)
- Test coverage expansion (#2)
- Performance testing โ Locust (#3)
- Serverless deployment (#6)
- WebSocket documentation (#1)
- CHANGELOG (#41)
- Health check endpoint
- Auto domain discovery
- Architecture enforcement (pre-commit)
- 13 AI development skills
Star this repo to follow our progress!
See CONTRIBUTING.md for development setup, coding guidelines, and PR process.
MIT License -- Free for commercial use, modification, and distribution.
