LCO Backend Documentation¶
Welcome to the LCO Construction Consulting API documentation. This documentation will help you understand, work with, and extend the FastAPI backend.
Documentation Index¶
For Developers New to FastAPI¶
Start here if you're coming from a frontend background or are new to FastAPI:
Quick Start Guide - 5-minute setup - FastAPI basics explained for frontend developers - Common tasks and examples - Troubleshooting guide
For Understanding the Architecture¶
Deep dive into how the backend is structured:
Architecture Guide - Complete architectural overview - Layer-by-layer breakdown - Design patterns and best practices - Data flow and integration patterns - Advanced topics
API Specifications¶
OpenAPI/Swagger specifications for the API:
API Spec - Complete - Full API specification - All endpoints and schemas
API Spec - Crew Builder - Crew-specific endpoints - Crew builder workflow
Crew Builder Flow - Crew creation workflow - Business logic documentation
Quick Links¶
Local Development¶
- API Documentation: http://localhost:8000/api/docs (Swagger UI)
- Alternative Docs: http://localhost:8000/api/redoc (ReDoc)
- Health Check: http://localhost:8000/api/v1/health
- OpenAPI Spec: http://localhost:8000/api/openapi.json
Key Project Files¶
- Main Application:
backend/app/main.py(FastAPI app with lifespan management) - Configuration:
backend/app/core/config.py(Pydantic Settings) - Database Client:
backend/app/db/cosmos.py(Async Cosmos DB client) - Base Repository:
backend/app/repositories/base.py(Generic CRUD operations) - Base Schemas:
backend/app/schemas/base.py(Pydantic base models)
Getting Started¶
# Navigate to backend directory
cd backend
# Create virtual environment (recommended)
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Setup environment variables
cp .env.example .env
# Edit .env with your Cosmos DB credentials
# Run development server with hot reload
uvicorn app.main:app --reload --port 8000
# Or use the npm script from project root
npm run dev:backend
Architecture Overview¶
The LCO backend follows a clean, layered architecture:
┌─────────────────────────────────────────┐
│ Frontend (React/Vite) │
└─────────────────────────────────────────┘
↓ HTTP/JSON
┌─────────────────────────────────────────┐
│ API Layer (FastAPI Routers) │ ← HTTP endpoints
├─────────────────────────────────────────┤
│ Schema Layer (Pydantic Models) │ ← Validation & serialization
├─────────────────────────────────────────┤
│ Repository Layer (Data Access) │ ← Business logic & queries
├─────────────────────────────────────────┤
│ Database Layer (Cosmos DB) │ ← Connection management
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Azure Cosmos DB (NoSQL) │
└─────────────────────────────────────────┘
Key Technologies¶
- Python: 3.12+ recommended (3.10+ minimum, modern type hints and generics)
- Framework: FastAPI 0.104+ (async web framework)
- Database: Azure Cosmos DB (NoSQL with SQL API)
- Database SDK: azure-cosmos 4.5.0+ (async client)
- Validation: Pydantic 2.5+ with pydantic-settings 2.1.0+
- HTTP Client: httpx 0.26.0+
- Testing: pytest 7.4.0+ with pytest-asyncio
- Code Quality: ruff, mypy
- Deployment: Azure Functions (ASGI wrapper)
Core Concepts¶
1. Layered Architecture¶
Each layer has a specific responsibility:
API Layer (app/api/v1/routers/)
- Define HTTP endpoints
- Handle request/response
- HTTP-specific concerns (status codes, headers)
Schema Layer (app/schemas/)
- Define data models
- Validate input/output
- Serialize to/from JSON
Repository Layer (app/repositories/)
- Abstract database operations
- Execute queries
- Transform data
Database Layer (app/db/)
- Manage connections
- Initialize containers
- Health checks
2. Repository Pattern¶
Separates data access from business logic:
# Repository handles data access
class ClientRepository(BaseRepository):
async def get_client(self, client_id: str) -> Optional[Client]:
# Database query logic
pass
# Router uses repository
@router.get("/{client_id}")
async def get_client(
client_id: str,
repo: ClientRepository = Depends(get_repo)
):
return await repo.get_client(client_id)
3. Dependency Injection¶
FastAPI's DI system manages dependencies:
# Define dependency
async def get_client_repo() -> ClientRepository:
return ClientRepository(cosmos_db.get_container("clients"))
# Inject into endpoints
@router.get("/{id}")
async def get_client(
id: str,
repo: ClientRepository = Depends(get_client_repo) # ← Injected
):
return await repo.get_client(id)
4. Async/Await¶
All I/O operations are non-blocking:
# Database operations
client = await repo.get_client(id) # Non-blocking
clients = await repo.get_all_clients() # Non-blocking
Domain Model¶
Core Entities¶
Clients (/api/v1/clients)
- Construction clients
- Parent of projects
Projects (/api/v1/projects)
- Construction projects
- Belongs to client
- Has many services
Services (/api/v1/projects/{projectId}/services)
- Services on projects
- Types: Estimation, Loan Monitoring, Project Controls, Claims
Crews (/api/v1/crews)
- Three-tier system for flexible crew management:
- Crew Trades (/crews/trades): Universal trade definitions (Carpenter, Electrician, etc.)
- Crew Members (/crews/members): Location/time/project-specific rate cards
- Crews (/crews): Reusable crew templates with manpower and equipment composition
Service Crews (/api/v1/service-crews)
- Links crews to services with project-specific context
- Stores indirect costs (labor and equipment breakdowns)
- Allows rate overrides per service
Equipment (/api/v1/equipment)
- Equipment catalog with base unit prices
- Categories: heavy, light, tools, vehicles
Material Takeoff (MTO) (/api/v1/mto)
- Discipline-based material takeoff sheets (9 disciplines)
- Async import with progress tracking (see MTO Import/Export Guide)
- Excel export (multi-sheet and single-sheet)
- Supports estimation services
- Items grouped by discipline
Subcontractors (/api/v1/projects/{projectId}/subcontractors)
- Project-specific subcontractor rate cards
- Track all-in rates for cost calculations
- Endpoints: List, Create, Get, Update, Delete, Batch Import
Labor Productivity Settings (/api/v1/services/{serviceId}/labor-settings)
- Per-discipline labor productivity factors
- Applied to labor cost calculations in MTO
- Endpoints: GET (auto-creates defaults), PUT
Project Material Costs (/api/v1/project-material-costs)
- Links MTO items to Material catalog
- Supports cost overrides per project/item
- Endpoints: List all, by project, by material, by item, CRUD operations
Entity Relationships¶
Client
└─ Project (many)
└─ Service (many)
└─ ServiceCrew (many)
└─ Crew (reference)
└─ CrewMember (many)
└─ CrewTrade (reference)
API Conventions¶
Endpoints¶
Pattern: /{resource} or /{parent}/{parentId}/{resource}
Examples:
- GET /api/v1/clients - List clients
- GET /api/v1/clients/{id} - Get client
- POST /api/v1/clients - Create client
- PUT /api/v1/clients/{id} - Update client
- DELETE /api/v1/clients/{id} - Delete client
- GET /api/v1/projects/{projectId}/services - List services for project
Request/Response Format¶
List Endpoints (Paginated):
Single Resource:
Error Response:
Field Naming¶
- Backend (Python):
snake_case - Frontend (JSON):
camelCase - Pydantic aliases handle conversion automatically
class Client(BaseModel):
client_name: str = Field(alias="clientName")
# Python code uses client_name
# JSON uses clientName
Common Workflows¶
Creating a New Entity¶
-
Define Schema (
app/schemas/entity.py) -
Add Repository Method (
app/repositories/entity.py) -
Add Router Endpoint (
app/api/v1/routers/entities.py) -
Test at http://localhost:8000/api/docs
Querying Data¶
# Simple query
query = "SELECT * FROM c WHERE c.type = @type"
parameters = [{"name": "@type", "value": "client"}]
results = await repo.query(query, parameters)
# With pagination
result = await repo.query_with_pagination(
query=query,
parameters=parameters,
skip=0,
limit=20
)
# Returns: { data: [...], total: 100, hasMore: true }
Error Handling¶
try:
entity = await repo.get_entity(id)
if not entity:
raise HTTPException(status_code=404, detail="Not found")
return entity
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
logger.error(f"Error: {e}")
raise HTTPException(status_code=500, detail="Internal error")
Development Guidelines¶
Code Style¶
- Follow PEP 8 conventions
- Use type hints everywhere
- Document with docstrings
- Keep functions focused and small
Naming Conventions¶
- Functions/variables:
snake_case - Classes:
PascalCase - Constants:
UPPER_SNAKE_CASE - Private:
_leading_underscore
File Organization¶
- One router per resource
- One repository per entity
- Group related schemas
- Keep utilities separate
Testing¶
- Use pytest for testing
- Write unit tests for repositories
- Integration tests for endpoints
- Mock Cosmos DB for testing
Environment Variables¶
Required in .env:
# Cosmos DB
COSMOS_ENDPOINT=https://your-account.documents.azure.com:443/
COSMOS_KEY=your-primary-key
DATABASE_NAME=lco-construction
# API
API_V1_STR=/api/v1
PROJECT_NAME=LCO Construction Consulting API
VERSION=2.0.0
# CORS
BACKEND_CORS_ORIGINS=http://localhost:3000,http://localhost:5173
# Azure Blob Storage
AZURE_STORAGE_CONNECTION_STRING=your-storage-connection-string
AZURE_STORAGE_CONTAINER_NAME=imports
# Azure Service Bus
AZURE_SERVICE_BUS_CONNECTION_STRING=your-service-bus-connection-string
AZURE_SERVICE_BUS_QUEUE_NAME=import-queue
Troubleshooting¶
Common Issues¶
"ModuleNotFoundError"
- Ensure you're in the backend directory
- Activate virtual environment
- Run pip install -r requirements.txt
"Cosmos DB connection failed"
- Check COSMOS_ENDPOINT in .env
- Verify COSMOS_KEY is correct
- Ensure Cosmos DB account exists
"CORS error in frontend"
- Add frontend URL to BACKEND_CORS_ORIGINS
- Restart backend server
- Check browser console for actual error
"Validation error" - Check request body matches schema - Verify field names (camelCase in JSON) - Check required fields are present
Deployment¶
Azure Functions¶
The backend deploys to Azure Functions via function_app.py:
import azure.functions as func
from app.main import app as fastapi_app
app = func.AsgiFunctionApp(
app=fastapi_app,
http_auth_level=func.AuthLevel.ANONYMOUS
)
See DEPLOYMENT_GUIDE.md and AZURE_FUNCTIONS_DEPLOY.md for details.
Resources¶
Official Documentation¶
Internal Documentation¶
- Architecture Guide - Deep dive into backend architecture
- Quick Start - Getting started with FastAPI
- MTO Import/Export Guide - Comprehensive MTO system documentation (async import, export, workflows)
- API Specs - OpenAPI spec
Tools¶
- Swagger UI - Interactive API docs
- ReDoc - Beautiful API docs
- Health Check - Service status
Contributing¶
Before Submitting Changes¶
- Test endpoints in Swagger UI
- Ensure all async functions are awaited
- Add type hints to new functions
- Update documentation if needed
- Follow existing code patterns
Code Review Checklist¶
- [ ] Type hints present
- [ ] Error handling implemented
- [ ] Async/await used correctly
- [ ] Pydantic models for validation
- [ ] Dependency injection used
- [ ] Documentation updated
- [ ] Tested in Swagger UI
Support¶
For questions or issues:
- Check this documentation
- Review the Architecture Guide
- Read the Quick Start
- Check Swagger UI for endpoint details
- Review existing similar code
Last Updated: 2025-11-27
Version: 2.1.0
Maintained By: LCO Development Team