This document explains how the FireForm repository is organized and, when you add new code, where it should go.
FireForm/
├── app/ # Backend source code (FastAPI application)
├── tests/ # Test suite (pytest)
├── docker/ # Dockerfiles, compose files, env templates
├── scripts/ # Shell scripts
├── docs/ # Project documentation
├── examples/ # Standalone demo scripts
├── data/ # Local runtime data (gitignored contents)
├── Makefile # Entry point for all dev commands (`make help`)
└── requirements.txt # Python dependencies
| You want to… | Go to |
|---|---|
| Change backend behavior | app/ |
| Add or fix tests | tests/ |
| Change container setup, env vars | docker/ (see docker/README.md) |
| Change setup/bootstrap scripts | scripts/ |
| Add documentation | docs/ |
| Add dev commands | Makefile |
The backend follows a layered structure: routes → services → repositories → database. Requests enter through the API layer, business logic lives in services, and all database access goes through repositories.
app/
├── main.py # App factory: creates FastAPI app, wires middleware and routers
├── api/ # HTTP layer - request/response handling only, no business logic
│ ├── router.py # Aggregates all route modules into one router; main.py mounts this
│ ├── deps.py # Shared FastAPI dependencies (e.g. DB session injection)
│ ├── routes/ # One module per feature (forms.py, templates.py, …)
│ └── schemas/ # Pydantic request/response models, one module per feature
├── core/ # App-wide infrastructure
│ ├── config.py # All settings and env var reading nothing else reads os.environ
│ ├── lifespan.py # Startup/shutdown logic (DB init, etc.)
│ ├── logging.py # Logging configuration
│ └── errors/ # Custom exception classes (base.py) and handlers (handlers.py)
├── services/ # Business logic — LLM extraction, PDF filling, orchestration
├── db/ # Database engine/session (database.py) and repositories.py
├── models/ # SQLAlchemy ORM models
└── utils/ # Small generic helpers with no business logic
Adding a new API endpoint:
app/api/schemas/<...>.py- Pydantic models for the request and response bodiesapp/api/routes/<...>.py- the route handlers; keep them thin, delegate to a serviceapp/api/router.py- register the new router (oneinclude_routerline)- Business logic goes in
app/services/, not in the route handler
Adding business logic: app/services/. A service should not know about HTTP (no FastAPI imports) it takes plain data in and returns plain data, so it can be tested and reused independently.
Adding a database table: define the ORM model in app/models/models.py, and add its query/persistence functions to app/db/repositories.py. Services call repositories; routes never touch the database directly.
Adding a setting or env var: declare it in app/core/config.py and document it in docker/.env.example. Code elsewhere imports from config, never reads os.environ itself.
Adding a custom error: subclass in app/core/errors/base.py; map it to an HTTP response in app/core/errors/handlers.py.
Tests live in tests/, run with make test (pytest inside the app container). conftest.py holds shared fixtures. Name files test_<area>.py and mirror what you're testing: API endpoint tests alongside test_api.py, model/DB tests alongside test_model.py.
Note: Tests will be undergoing many changes hence the docs can be outdated, Please raise issue if tests or docs are outdated.
docker/dev/- development image (hot reload, source mounted) and its compose file. This is whatmake upruns.docker/prod/- production image (multi-stage build, gunicorn, no source mount).docker/.env.example- template for env vars; copied to.env.devbymake init.scripts/- shell scripts invoked bymake init(dependency checks, env file creation, model selection) and by container startup. Not meant to be run directly.
Full details: docker/README.md.
examples/- runnable demo scripts showing the pipeline end to end; not imported by the app.data/- runtime working data on your machine; contents are not part of the codebase.src/,temp/- scratch/legacy directories slated for cleanup; don't add new code here.