Skip to content

Backend Overview

Summary

Poolia.BACK is a FastAPI-based backend for a pool management platform. It exposes REST endpoints for authentication, users, pools, pool readings, visits, technician invitations, photo uploads, and a role-aware messaging system. Data is stored in MongoDB and modeled with Beanie documents; file uploads are stored in Azure Blob Storage.

The codebase follows a conventional layered structure:

  • API routers define HTTP endpoints and request/response contracts.
  • Services contain business rules and cross-entity workflows.
  • Repositories encapsulate persistence access for Beanie documents.
  • Models define MongoDB documents.
  • Schemas define request and response payloads.
  • Core contains configuration, database bootstrapping, and security helpers.

Stack

  • Framework: FastAPI
  • ASGI server: Uvicorn
  • Database: MongoDB
  • ODM: Beanie
  • Validation / settings: Pydantic v2 + pydantic-settings
  • Authentication: JWT with python-jose
  • Password hashing: Passlib + bcrypt, with SHA-256 prehashing before bcrypt
  • File storage: Azure Blob Storage
  • Local orchestration: Docker Compose

Runtime architecture

Application entry point

The application starts in app.main.

Main responsibilities:

  • instantiate the FastAPI app
  • configure permissive CORS
  • register all routers
  • initialize the database on startup
  • expose a basic /health endpoint

Startup flow

  1. FastAPI application is created with settings.app_name.
  2. CORS middleware is added with allow_origins=["*"], allow_methods=["*"], and allow_headers=["*"].
  3. Routers are mounted under their prefixes.
  4. On startup, init_db() initializes Beanie against MongoDB.
  5. The service begins accepting requests.

Project structure

backend/
  app/
    api/
      dependencies/
      routers/
    core/
      config.py
      db.py
      security.py
    models/
    repositories/
    schemas/
    scripts/
    services/
    main.py
  requirements.txt

Configuration

The backend uses a Settings class derived from BaseSettings and reads values from environment variables or .env.

Current primary settings:

  • mongo_uri
  • mongo_db
  • app_name
  • blob_service_connection_string
  • blob_service_container_name

This configuration is intentionally small and focused on infrastructure and naming.

Persistence model

MongoDB is accessed through Motor and initialized through Beanie. The application registers the following document models during startup:

  • User
  • Pool
  • PoolValues
  • Visit
  • Invite
  • Photo
  • AlertConfig
  • MessageThread
  • Message

This means the backend stores both operational pool data and communication/media data in the same MongoDB database, while binary file content lives in Azure Blob Storage.

Security model

Authentication

Authentication is token-based:

  • /auth/register creates a user and returns a bearer token
  • /auth/login validates credentials and returns a bearer token
  • protected routes use OAuth2PasswordBearer(tokenUrl="auth/login")
  • token decoding resolves the current user by the JWT sub claim

Password handling

Passwords are not hashed directly with bcrypt. Instead, the code first computes a SHA-256 digest and then hashes that digest with bcrypt. Verification applies the same prehashing strategy before comparison.

Authorization

Authorization is currently implemented selectively:

  • messaging routes require an authenticated user
  • GET /users/me and POST /users/me/change-password require an authenticated user
  • the messaging service enforces role-aware access to pools (admin, owner, technician)

A large portion of CRUD endpoints for users, pools, visits, invites, and pool values is currently exposed without authentication guards at the router layer. That is important to document because it reflects the present implementation, not an aspirational design.

Domain areas

Users and auth

Users have a role (admin, owner, technician) and a lifecycle status (active, blocked, pending, deleted). Auth is JWT-based and login updates the user's last login timestamp.

Pools

Pools are the central business entity. A pool belongs to one owner, may have multiple assigned technicians, has a geographic location, and can optionally store physical attributes such as volume and depth.

Pool values and visits

Water quality readings live in PoolValues. Visits track technician activity against a pool and may reference one pool-values record. There is also a convenience workflow that creates both a visit and a pool-values snapshot together.

Alert configuration

Each pool can have threshold configuration for pH, chlorine, and temperature. If no configuration exists, the service creates a default one on demand.

Invites

Owners can invite technicians to pools. Invites carry a status (pending, accepted, declined) and default expiration logic.

Photos

Photos may be attached to users, visits, pools, threads, or individual messages. The metadata is stored in MongoDB; the file object itself is uploaded to Azure Blob Storage.

Messaging

The messaging subsystem is the most structured role-aware area in the project. It models discussion threads per pool, supports messages of type question and answer, tracks cached last-message data on the thread, and allows attaching photos to individual messages.

Layering and responsibilities

Routers

Routers are intentionally thin. They mainly:

  • receive validated input
  • inject dependencies such as current_user
  • delegate to services
  • declare response models and status codes

Services

Services implement the real business rules, such as:

  • owner / technician validation for pool assignment
  • preventing duplicate pending invites
  • pairing a visit with a water-reading snapshot
  • role-based access checks for messaging
  • photo upload orchestration and metadata persistence
  • default alert-config creation

Repositories

Repositories isolate persistence calls so that services do not directly encode every Beanie query. This keeps services focused on validation and orchestration.

Notable implementation characteristics

Positive patterns

  • Clear separation between routers, services, and repositories
  • Pydantic aliases are used consistently for API-facing camelCase fields
  • Messaging includes meaningful authorization checks and thread cache updates
  • Azure Blob access was moved toward lazy initialization in services, which is friendlier for scripts and documentation generation
  • Generated documentation scripts were added under app/scripts, which keeps automated documentation close to the backend codebase

Current limitations and caveats

  • CORS is fully open
  • Many non-messaging routes are not yet protected by authentication dependencies
  • Error messages mix English and Spanish
  • Some endpoint naming uses both snake_case and camelCase path parameters (invite_id vs inviteId, pool_id vs poolId)
  • The logout endpoint is currently only a placeholder response and does not revoke tokens
  • Configuration is small and infrastructure-focused, but secrets still need proper environment management outside development

Local development

The repository includes a Docker Compose setup with:

  • mongo:7 exposed on local port 27018
  • the FastAPI application exposed on port 8000
  • a bind mount of ./backend into /app
  • Uvicorn running with --reload

This makes the project easy to run locally and aligns well with iterative API development.

Documentation strategy

The backend now supports automatic documentation artifact generation. The codebase already includes scripts to export:

  • OpenAPI JSON
  • OpenAPI Markdown
  • models reference
  • router map
  • services map
  • settings reference

Those artifacts can be published into the separate Poolia.DOCS repository, while the manual overview, API, and code-reference pages remain human-written entry points.

For someone new to the project, the best path is:

  1. Read this overview
  2. Read the API page for external behavior
  3. Read the code reference page for internal structure
  4. Use generated docs for precise technical lookup