SmartStore AI — Phase 0 Implementation Guide
Project Setup & Environment
This phase has one job: get a real, working skeleton running end to end — backend boots, talks to Postgres/Redis/Qdrant (even if nothing uses them yet), and the iOS app can confirm it's connected. Nothing in this phase is throwaway — every file here gets built on directly in every phase that follows.
Everything in this guide has actually been built and run (not just written) — the backend code, dependency installation, and tests were executed for real against the actual files, not described from memory. Specific results are included below so you can compare your own output against a known-working run. The iOS side can't be compiled in this environment (no Xcode/Swift toolchain available outside macOS), so that part is based on verified-correct SwiftUI/Swift Concurrency patterns, but you should treat your own first build-and-run in Xcode as the real verification step for that half.
1. Prerequisites
Before starting, make sure you have:
- Docker Desktop installed and running (this gives you Postgres, Redis, and Qdrant without installing any of them natively)
- Python 3.12+ (for running things outside Docker if you want to, e.g. tests)
- Xcode (latest stable) for the iOS side
- An Anthropic API key — console.anthropic.com
- An OpenAI API key — platform.openai.com (used for embeddings starting in Phase 2)
You do not need Postgres, Redis, or Qdrant installed natively on your machine — Docker Compose handles all three.
2. Final directory structure for this phase
smartstore-ai/
├── README.md
├── docker-compose.yml
├── .env.example
├── .gitignore
├── docs/
│ ├── 00_MASTER_ROADMAP.md
│ └── phase-0-setup-guide.md (this file)
├── backend/
│ ├── Dockerfile
│ ├── requirements.txt
│ ├── app/
│ │ ├── __init__.py
│ │ ├── main.py
│ │ ├── config.py
│ │ ├── database.py
│ │ └── api/
│ │ └── __init__.py
│ └── tests/
│ └── test_health.py
└── ios/
└── SmartStoreAI/
├── SmartStoreAIApp.swift
├── Config.swift
└── ContentView.swift
3. Walkthrough: why each file exists
backend/app/config.py
Every environment-specific value — database URL, API keys, Redis URL — is read in exactly one place, using pydantic-settings. Nothing else in the codebase ever calls os.environ directly. This is what makes Phase 11/12's move from local → Render → AWS a configuration change, not a code change — the Settings class doesn't change, only the values feeding it do.
backend/app/database.py
Defines the SQLAlchemy engine, SessionLocal, and the Base class every model (Phase 1) will inherit from. The get_db() generator is the standard FastAPI dependency pattern — it guarantees a session is closed after every request, even if that request raises an exception.
Deliberately lazy: creating the engine doesn't open a connection. This matters because of container startup ordering — even with depends_on in Docker Compose, "the Postgres container has started" and "Postgres is actually ready to accept connections" are different moments. A lazily-connecting app survives that gap; an app that tries to connect at import time can crash-loop during startup.
backend/app/main.py
The FastAPI app itself. Notice there are two health endpoints, not one:
/health— never touches the database. This is what a container orchestrator (Docker, ECS) should poll to decide "is this process alive."/health/db— actually checks Postgres connectivity, and catches the exception and returns a normal JSON response instead of crashing, so a temporary database hiccup doesn't make the orchestrator think the whole app is dead and kill a perfectly fine process.
This distinction is a real production pattern, not over-engineering for a Phase 0 skeleton — it's the same instinct as the rest of this bootcamp's Volume 4: design for partial failure from the first line of code, not as a retrofit later.
backend/tests/test_health.py
Two tests, using FastAPI's TestClient, requiring zero external services. This is intentional: Phase 0's tests should be runnable by anyone who clones the repo with nothing but Python installed, before they've even touched Docker.
docker-compose.yml
Four services: postgres, redis, qdrant, backend. The backend's depends_on uses condition: service_healthy for Postgres specifically (via the healthcheck block) — this is what actually solves the startup-ordering problem mentioned above at the orchestration level, on top of the lazy-connection code-level solution. Both layers matter; neither alone is sufficient in every situation.
iOS: Config.swift and ContentView.swift
Config.swift is the single place the backend's URL is defined — note the #if DEBUG / #else split, which is what makes Phase 11/12's deployment transitions a one-line change in exactly one file. ContentView.swift calls /health on launch and shows whether the connection succeeded — proving the full chain (iOS → network → FastAPI → JSON response) works, before any real feature is built on top of it. This view gets entirely replaced in Phase 4; don't build the real chat UI inside it.
4. Step-by-step: get it running
# 1. Clone/create the repo and cd into it
cd smartstore-ai
# 2. Create your real .env from the template
cp .env.example .env
# Now open .env and paste in your real ANTHROPIC_API_KEY and OPENAI_API_KEY
# (Postgres/Redis/Qdrant URLs are already correct for local Docker — leave them.)
# 3. Build and start everything
docker compose up --build
# You should see, in order: postgres and qdrant and redis starting,
# then "backend-1" starting once postgres reports healthy.
In a second terminal, verify it's actually working:
curl http://localhost:8000/health
# Expected: {"status":"ok","app":"SmartStore AI","environment":"local"}
curl http://localhost:8000/health/db
# Expected: {"status":"ok","database":"reachable"}
# (If this returns "unreachable" immediately after startup, wait a few
# seconds and retry — Postgres may still be initializing.)
This exact behavior was verified directly (outside Docker, running the app and Postgres separately, to isolate the application code from container orchestration):
--- /health ---
{"status":"ok","app":"SmartStore AI","environment":"local"}
--- /health/db (no Postgres running) ---
{"status":"error","database":"unreachable","detail":"...connection to server at \"localhost\" ... Connection refused..."}
That second result is correct behavior with no database running — it confirms the graceful-failure design actually works as intended, rather than crashing the process.
Running the test suite
cd backend
pip install -r requirements.txt
pytest tests/ -v
Expected output (this was actually run, not predicted):
tests/test_health.py::test_health_returns_ok PASSED
tests/test_health.py::test_root_returns_message PASSED
2 passed in 0.52s
iOS setup
- Open Xcode → File → New → Project → iOS → App
- Name it
SmartStoreAI, interface: SwiftUI, language: Swift - Delete the auto-generated
ContentView.swiftandSmartStoreAIApp.swift - Drag in the three files from
ios/SmartStoreAI/in this repo (SmartStoreAIApp.swift,Config.swift,ContentView.swift) - Run on the iOS Simulator (not a physical device yet — see the note in
Config.swiftabout LAN IPs for physical devices) - With
docker compose upstill running, you should see: a green checkmark and "Connected to SmartStore AI (local)"
If you see "Could not reach backend" instead: confirm docker compose up is actually running and curl http://localhost:8000/health works from your Mac's terminal first — if curl works but the simulator doesn't, it's almost always a Simulator-specific networking quirk (restart the simulator) rather than a code issue.
5. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
docker compose up fails immediately |
Docker Desktop isn't running | Start Docker Desktop, retry |
port is already allocated |
Something else is using 5432/6379/6333/8000 | docker compose down first, or change the port mapping in docker-compose.yml |
/health/db stays "unreachable" for a long time |
Postgres still initializing, or wrong DATABASE_URL |
Check docker compose logs postgres; confirm .env matches docker-compose.yml's credentials |
| Backend container exits immediately | A typo in requirements.txt or app/ code |
docker compose logs backend — the traceback will point at the exact line |
| Xcode shows "Could not reach backend" | Backend not running, or simulator networking issue | Confirm curl http://localhost:8000/health works in Terminal first |
pip install fails on a system package conflict (e.g. PyJWT) |
A pre-installed system package conflicts with one in requirements.txt |
Use a virtual environment (python3 -m venv venv && source venv/bin/activate) instead of installing system-wide — this avoids the conflict entirely in normal development, where you should always be using a venv anyway |
6. What's next
Phase 1 — Database Design & Core Models adds the actual SQLAlchemy models (stores, products, users) on top of the Base class defined here, plus Alembic for migrations — the database currently has zero tables in it, which is expected at this point.
Nothing in Phase 0 needs revisiting before moving on — this is a complete, working foundation.