Skip to content

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


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):

{
  "data": [...],
  "total": 100,
  "skip": 0,
  "limit": 20,
  "hasMore": true
}

Single Resource:

{
  "id": "abc-123",
  "createdAt": "2025-10-09T12:00:00Z",
  "updatedAt": "2025-10-09T12:00:00Z",
  ...
}

Error Response:

{
  "detail": "Client not found"
}

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

  1. Define Schema (app/schemas/entity.py)

    class EntityCreate(BaseModel):
        name: str
        description: Optional[str] = None
    

  2. Add Repository Method (app/repositories/entity.py)

    async def create_entity(self, data: EntityCreate) -> Entity:
        entity_dict = data.model_dump(by_alias=True)
        result = await self.create(entity_dict)
        return Entity(**result)
    

  3. Add Router Endpoint (app/api/v1/routers/entities.py)

    @router.post("", response_model=Entity, status_code=201)
    async def create_entity(
        data: EntityCreate,
        repo: EntityRepository = Depends(get_repo)
    ):
        return await repo.create_entity(data)
    

  4. 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

Tools


Contributing

Before Submitting Changes

  1. Test endpoints in Swagger UI
  2. Ensure all async functions are awaited
  3. Add type hints to new functions
  4. Update documentation if needed
  5. 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:

  1. Check this documentation
  2. Review the Architecture Guide
  3. Read the Quick Start
  4. Check Swagger UI for endpoint details
  5. Review existing similar code

Last Updated: 2025-11-27

Version: 2.1.0

Maintained By: LCO Development Team