A full-stack e-commerce backend built with FastAPI (Clean Architecture), HTMX frontend, SQLite, .NET Aspire orchestration, and Mailpit email capture.
On command prompt, start with: (selected OpenCode and Windsurf, I use Windsurf(devin) later to validate what Opencode/Openspec did)
openspec initThen fire up OpenCode:
opencode .Then start openspec prompt with /opsx-propose with this:
using knowledge from skill 'clean-architecture-python' create a FastAPI application
to build an e-commerce order and inventory system with full auth. We will start by using SQLite. I want .Net
Aspire to orchestrate and monitor the FastApi application. If emailing will be used, instead if Sendgrid, I would
like to use Mailpit.
Note
This is still a work in progress as I am learning this slew of tools that are pouring in our daily feeds for knowledge...
- Prompts Given During Development
- AI Tools Involved
- Technologies Used
- Architecture
- Getting Started
- Deploying to Azure
- Updating Libraries
- Suggested Next Steps
All prompts are listed in chronological order as they were issued to the AI assistant:
-
Design an e-commerce order and inventory management system — Initial project proposal; the AI designed the system, produced specs (user auth, product catalog, shopping cart, order management, inventory tracking, email notifications, Aspire orchestration), and generated a plan.
-
"I want to implement it" — Triggered full implementation of all planned phases.
-
"archive" — Archived the completed change after implementation and verification.
-
"how to run the tests?" — Asked how to execute the test suite.
-
"Let's use port 5566 instead of 8000" — Changed the application port from 8000 to 5566 across all configuration files.
-
"the docker build is failing" — Fixed Dockerfile build order (copy
pyproject.tomlbefore source files for better layer caching). -
"Add a frontend with HTMX, Jinja2, Alpine.js, Tailwind CSS" — Requested a server-rendered HTML frontend; resulted in 27 HTML routes, 14 Jinja2 templates, and a full admin dashboard.
-
"The admin user should be forced to change password on first login" — Added
must_change_passwordcolumn and Alembic migration, enforced in login flow. -
"What did we do so far?" — Asked for a project status summary and milestone archive.
-
"Fix two issues: error parameter in Jinja2 templates is not being passed consistently + RedirectResponse in dependencies causes problems" — Fixed missing
errorcontext in template rendering and replacedRedirectResponseraises withResponseheaders. -
"When I access the root, I should see a landing page" — Created
templates/index.htmlwith hero section, category grid, and featured products. -
Error running the app (Jinja2 TypeError) — Diagnosed and fixed
TypeError: cannot use 'tuple' as a dict keycaused by incorrectTemplateResponseargument order. -
Fix TemplateResponse argument order + API prefix + route ordering — Fixed
(request, name, context)signature, moved API routes under/apiprefix, reordered HTML router before API router. -
"What is the admin user email and password?" — Asked for admin credentials (
admin@shop.com/admin123). -
"If there are no products, seed with 100 mock products on startup" — Added 100 products across 8 categories with inventory, seeded at startup if the database is empty.
-
"Protect the /docs URL, only allow admin role" — Added middleware to protect
/docs,/redoc, and/openapi.json. -
"Use HTTP Basic Authentication for the docs" — Changed docs protection to HTTP Basic Auth verified against the
userstable. -
"Can the endpoints be categorized or grouped in Swagger?" — Added OpenAPI tags to all routes for Swagger/ReDoc grouping.
-
"Rename the 'default' tag to 'HTMx-FrontEnd'" — Renamed the default tag to
HTMx-FrontEndfor clarity. -
"Update the Aspire project to start the FastAPI application as a Python project instead of a Docker image" — Switched from
AddContainer(Docker) toAddExecutable(direct Python process) inAppHost.cs. -
"Is this OTEL trace export error something to worry about?" — Confirmed
localhost:4317connection refused is harmless during local dev without an OpenTelemetry collector. -
"Can the authorize method in Swagger ask for user and password instead of bearer?" — Added HTTP Basic Auth security scheme alongside Bearer in the OpenAPI spec so Swagger's Authorize button accepts email/password.
-
"Write a README.md with all the prompts I have given to you" — This document.
| Tool | Role |
|---|---|
| OpenCode(with openspec) | Primary AI coding assistant — wrote all code, ran commands, debugged issues across the full stack |
| GSD Skills (gsd-new-project, gsd-discuss-phase, gsd-plan-phase, gsd-execute-phase, gsd-verify-work, gsd-archive-change) | Structured development workflow: project initialization, phase discussion, planning, execution, verification, and archiving |
| brainstorming skill | Explored design options and requirements before implementation |
| dotnet-api skill | Guided .NET Aspire project structure and AppHost configuration |
| fastapi skill | Provided FastAPI best practices for route design, dependencies, and middleware |
| Technology | Version | Purpose |
|---|---|---|
| Python | 3.12+ | Runtime |
| FastAPI | 0.115+ | Web framework (async, OpenAPI auto-docs) |
| Uvicorn | 0.32+ | ASGI server |
| SQLAlchemy | 2.0+ | ORM / database access |
| Alembic | 1.14+ | Database migrations |
| Pydantic | 2.10+ | Data validation / settings management |
| python-jose | 3.3+ | JWT encoding/decoding |
| passlib[bcrypt] | 1.7+ | Password hashing |
| Jinja2 | 3.1+ | Server-side HTML template rendering |
| python-multipart | 0.0.9+ | Form data parsing for login/register |
| httpx | 0.28+ | HTTP client (email sending to Mailpit) |
| OpenTelemetry | 1.29+ | Distributed tracing / metrics |
| Technology | Purpose |
|---|---|
| HTMX | AJAX-driven dynamic updates (loaded via CDN) |
| Alpine.js | Lightweight JavaScript interactivity (loaded via CDN) |
| Tailwind CSS | Utility-first CSS framework (loaded via CDN) |
| Technology | Purpose |
|---|---|
| SQLite | Development database (single file, zero config) |
| Docker | Containerization (app + Mailpit) |
| .NET Aspire | Local orchestration dashboard (OTEL tracing, logs) |
| Mailpit | Local email capture (SMTP + web UI on port 8025) |
| pytest | Test framework |
The application follows Clean Architecture with strict dependency inversion — inner layers never import from outer layers.
┌──────────────────────────────────────────────┐
│ Framework Layer │
│ (FastAPI, SQLAlchemy, JWT, Uvicorn, DI) │
│ src/framework/ │
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ Interface Adapters Layer │ │
│ │ (REST controllers, serializers) │ │
│ │ src/interface_adapters/ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────┐ │ │
│ │ │ Application Layer │ │ │
│ │ │ (Use cases, DTOs, Ports, Result) │ │ │
│ │ │ src/application/ │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────────────────────────────┐ │ │ │
│ │ │ │ Domain Layer │ │ │ │
│ │ │ │ (Entities, Value Objects, │ │ │ │
│ │ │ │ Domain Events) │ │ │ │
│ │ │ │ src/domain/ │ │ │ │
│ │ │ └──────────────────────────────────┘ │ │ │
│ │ └──────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ Infrastructure Layer │ │
│ │ (SQLAlchemy repos, Mailpit email) │ │
│ │ src/infra/ │ │
│ └──────────────────────────────────────────┘ │
└──────────────────────────────────────────────┘
Frontend Layer (HTMX + Jinja2)
src/frontend/html_router.py
templates/
| Layer | Dependencies | Key Contents |
|---|---|---|
| Domain | None (pure Python) | entities/models.py — User, Product, Order, Cart, Inventory entities; value_objects/common.py — Email, Money, Address; events/events.py — domain events |
| Application | Domain only | use_cases/ — 5 use case files (auth, cart, inventory, order, product); dto/dtos.py — request/response DTOs; ports/ — 6 repository ABCs; result/ — Result[T] type |
| Interface Adapters | Application | controllers/api.py — 24 REST endpoints; serializers |
| Infrastructure | Application | repositories/ — 5 SQLAlchemy repo implementations; email/mailpit_adapter.py — Mailpit email sender |
| Framework | All above + FastAPI | auth/ — JWT + dependencies; db/models.py — SQLAlchemy ORM models; db/session.py — engine + session; di/composition_root.py — FastAPI app factory, DI wiring, middleware, seed data |
| Frontend | Application + Jinja2 | html_router.py — 27 HTML routes; templates/ — 14 Jinja2 templates |
- Result type over exceptions: Every use case returns
Result.success(value)orResult.failure(error). No exception flow for business logic. - Session cookie auth for HTML: JWT-wrapped
session_tokencookie for browser users; Bearer tokens for API clients. - Swagger accepts both Bearer and Basic Auth: The
Authorizebutton shows username/password fields (Basic Auth) and a token field (Bearer JWT). - HTML routes before API routes: Router include order prevents path conflicts; API routes live under
/apiprefix. - Docs protected by middleware-level HTTP Basic Auth: Checked against
userstable (admin role only), not route-level dependencies. - Auto-seeding on startup: Admin user + 100 products across 8 categories + inventory records created if the database is empty.
- Aspire via AddExecutable: Python process runs directly (not in Docker) for faster local iteration; environment variables configured inline.
- users —
id,email,hashed_password,role,must_change_password, timestamps - categories —
id,name,description - products —
id,sku,name,description,price,category_id,status, timestamps - carts —
id,user_id,session_id, timestamps - cart_items —
id,cart_id,product_id,quantity,unit_price - orders —
id,user_id,status,total, timestamps - order_line_items —
id,order_id,product_id,product_name,quantity,unit_price,line_total - inventory —
product_id,on_hand,reserved,low_stock_threshold - restock_events —
id,product_id,quantity,timestamp
| Group | Count | Prefix |
|---|---|---|
| Health | 1 | GET /api/health |
| Auth | 5 | POST /api/auth/register, /login, /refresh, /logout, GET /me |
| Categories | 2 | GET /api/categories, GET /api/categories/{id} |
| Products | 5 | CRUD + GET /api/products |
| Cart | 4 | GET /api/cart, POST /api/cart/items, PATCH /api/cart/items/{id}, DELETE /api/cart/items/{id} |
| Orders | 4 | GET /api/orders, POST /api/orders, GET /api/orders/{id}, POST /api/orders/{id}/cancel |
| Admin | 2 | POST /api/admin/products, PATCH /api/admin/products/{id} |
| Inventory | 1 | PATCH /api/inventory/{product_id} |
Plus 27 HTML routes under src/frontend/html_router.py for the HTMX frontend.
- Python 3.12+
- .NET 9+ SDK (only for Aspire orchestration; the app runs without it)
- Docker / Docker Compose (optional, for containerized deployment)
- Mailpit (optional, for email capture — auto-started via Docker or Aspire)
# 1. Create and activate virtual environment
python3.12 -m venv .venv
source .venv/bin/activate
# 2. Install dependencies
pip install -e ".[dev]"
# 3. Copy environment file
cp .env.example .env
# 4. Start Mailpit (optional — in another terminal)
docker run -d -p 8025:8025 -p 1025:1025 axllent/mailpit
# 5. Run the application
python -m src.framework.main
# or: uvicorn src.framework.main:app --reload --port 5566
# 6. Open in browser
# Main app: http://localhost:5566
# API docs: http://localhost:5566/docs (admin admin@shop.com / admin123)
# Mailpit UI: http://localhost:8025# Stop python app from above if started separately
cd aspire
dotnet run --project ECommerce.AppHost
# Opens dashboard at https://localhost:15888docker compose up --build| Field | Value |
|---|---|
admin@shop.com |
|
| Password | admin123 |
| Note | You'll be prompted to change password on first login |
pytest # All tests with coverage
pytest -v # Verbose
pytest --cov=src --cov-report=term-missing # Coverage report# Create a new migration
alembic revision --autogenerate -m "description"
# Apply migrations
alembic upgrade head
# Rollback one step
alembic downgrade -1
# View history
alembic history# Prerequisites
# az login
# az group create --name myEcommerceGroup --location westeurope
# Build and push to Azure Container Registry
az acr create --name myecommerceacr --resource-group myEcommerceGroup --sku Basic
az acr build --registry myecommerceacr --image ecommerce-api:latest .
# Deploy to Container Apps
az containerapp create \
--name ecommerce-api \
--resource-group myEcommerceGroup \
--environment myEnvironment \
--image myecommerceacr.azurecr.io/ecommerce-api:latest \
--target-port 5566 \
--ingress external \
--query properties.configuration.ingress.fqdn
# Set environment variables
az containerapp update \
--name ecommerce-api \
--resource-group myEcommerceGroup \
--set-env-vars \
DATABASE_URL="sqlite:///data/ecommerce.db" \
JWT_SECRET="<secure-random-secret>" \
OPENTELEMETRY_ENDPOINT="" \ # Disable OTEL in production
MAILPIT_URL="<your-mailpit-url>"
# Enable file share for SQLite persistence (optional)
az containerapp volume add \
--name ecommerce-api \
--resource-group myEcommerceGroup \
--storage-type AzureFile \
--mount-path /app/data# Create App Service (Linux, Python 3.12)
az webapp create \
--name ecommerce-api-app \
--resource-group myEcommerceGroup \
--runtime PYTHON:3.12 \
--sku B1
# Deploy via zip or configure CI/CD
az webapp config set \
--name ecommerce-api-app \
--resource-group myEcommerceGroup \
--startup-file "uvicorn src.framework.main:app --host 0.0.0.0 --port 8000"
az webapp config appsettings set \
--name ecommerce-api-app \
--resource-group myEcommerceGroup \
--settings \
DATABASE_URL="sqlite:///data/ecommerce.db" \
JWT_SECRET="<secure-random-secret>" \
OPENTELEMETRY_ENDPOINT=""# Create VM with Docker
az vm create \
--name ecommerce-vm \
--resource-group myEcommerceGroup \
--image UbuntuLTS \
--admin-username azureuser \
--generate-ssh-keys
# SSH in and run
docker compose up -d- Change
JWT_SECRETto a strong random value (useopenssl rand -hex 32) - Replace SQLite with PostgreSQL or Azure SQL for production
- Set up a real email provider (SendGrid, SMTP) instead of Mailpit
- Disable OpenTelemetry (
OPENTELEMETRY_ENDPOINT="") unless using an OTLP collector - Disable CORS
allow_origins=["*"]— restrict to your frontend domain - Set up proper logging (Azure Monitor / Application Insights)
- Configure a reverse proxy (Nginx / Caddy) for TLS termination
- Run database migrations via Alembic before deploying new versions
# Update all pinned dependencies to latest compatible versions
pip install --upgrade -e ".[dev]"
# Or use pip-tools for dependency pinning
pip install pip-tools
pip-compile --upgrade pyproject.toml
pip-sync requirements.txt
# Update a specific package
pip install fastapi@latest
# Check for outdated packages
pip list --outdatedcd aspire
# Check for outdated NuGet packages
dotnet list package --outdated
# Update all packages in the solution
dotnet update
# Update a specific package
dotnet add ECommerce.AppHost package Aspire.AppHost.Sdk --version <latest>
dotnet add ECommerce.ServiceDefaults package OpenTelemetry.Exporter.OpenTelemetryProtocol --version <latest>After updating, run the full test suite:
pytest- Replace SQLite with PostgreSQL — Use
asyncpg+ SQLAlchemy async engine for better concurrency and reliability. Add adocker-compose.ymlservice for PostgreSQL. - Add proper password reset flow — Email-based reset token with expiry, sent via Mailpit/SMTP.
- Add pagination to product listing —
GET /api/products?page=1&per_page=20with total count and next/prev links. - Add product search/filter — Full-text search on product name/description, filter by category, price range, sort by price/name.
- Add checkout flow — Address collection, order summary confirmation page, order placement with inventory deduction.
- Replace SQLite with Azure SQL Database or PostgreSQL (Azure Database for PostgreSQL Flexible Server) — Production-grade database with automated backups, geo-replication, and connection pooling.
- Add payment integration — Stripe Checkout or PayPal integration for order payment.
- Add real email provider — SendGrid, AWS SES, or SMTP2API for transactional emails (order confirmation, shipping updates, password reset).
- Add admin product CRUD UI — Image upload, rich text descriptions, inventory management form in the admin dashboard.
- Add user address management — Multiple saved addresses per user with default shipping/billing selection.
- Add order status workflow — State machine (pending → confirmed → shipped → delivered → cancelled) with validation at each transition.
- Add CI/CD pipeline — GitHub Actions or Azure DevOps for automated testing, linting, and deployment to Azure Container Apps.
- Add caching layer — Redis via
fastapi-cachefor product catalog, session storage, rate limiting. - Add rate limiting —
slowapior custom middleware to prevent abuse on auth and checkout endpoints. - Add webhook support — Send order status change webhooks to external systems.
- Add admin analytics dashboard — Sales charts, popular products, revenue trends, customer acquisition metrics.
- Add multi-tenant support — Organization-scoped products, orders, and users.
- Add API versioning — URL-based (
/api/v1/) or header-based versioning for backwards compatibility. - Add automated E2E tests — Playwright or Selenium tests for the HTMX frontend.
- Add load testing — Locust or k6 scripts for performance benchmarking.
- Add CDN for static assets — Move Tailwind/Alpine/HTMX from CDN to bundled static files with cache-busting.
- Add OpenAPI client generation — Generate TypeScript/Python client SDKs from the OpenAPI spec.
- Add Kubernetes manifests — Helm charts or Kustomize for production deployment to AKS.