{"slug": "fashion-e-commerce-store", "title": "Fashion E-Commerce Store", "summary": "A developer built a full fashion e-commerce platform using FastAPI, MySQL, and SQLAlchemy, featuring a rule-based size recommendation engine that matches user measurements to product size charts. The project includes product variants for size and color, JWT authentication, and a payment gateway abstraction with mock implementations.", "body_md": "When I started this project, the brief was simple to state and deceptively large to build: a full fashion e-commerce platform — product catalog, size and color variants, filters, an \"AI-style\" size recommender, cart, checkout, JWT auth, order tracking, and a wishlist. Here's a walkthrough of how it came together, the decisions behind the architecture, and a few lessons learned along the way.\n\nWhy FastAPI + MySQL\n\nFastAPI was the obvious choice for a REST backend like this. Automatic OpenAPI docs (you get /docs for free), native Pydantic validation, and async support made it easy to move fast without sacrificing structure. MySQL paired with SQLAlchemy's ORM gave a relational model that fits e-commerce data naturally — products, variants, orders, and users all have clean foreign-key relationships that a document store would make awkward.\n\nThe stack ended up being:\n\nBackend: FastAPI + Pydantic v2\n\nDatabase: MySQL 8.x via SQLAlchemy ORM, with Alembic for migrations\n\nAuth: JWT (OAuth2 password flow) + bcrypt password hashing\n\nFrontend: Server-rendered Bootstrap 5 pages with vanilla JS calling the same REST API a mobile app would use\n\nDesigning the catalog: the size/color variant problem\n\nThe trickiest early design decision was how to model a product that comes in multiple sizes and colors, each with its own stock count. The naive approach — a single stock field on the product — falls apart the moment you need \"Medium, Black\" to sell out while \"Large, Black\" is still available.\n\nThe fix was a separate ProductVariant table: one row per size+color combination, each with its own SKU and stock quantity. The Product table holds shared data (name, description, price, category), while ProductVariant handles everything that varies by selection. This is the same pattern most real fashion retailers use under the hood, and it made the cart and checkout logic much simpler — every cart line item points to a specific variant, not just a product.\n\nProduct (1) ──< ProductVariant (many)\n\n\"Classic Crew Neck T-Shirt\" S/Black, M/Black, L/Black,\n\nS/White, M/White, ...\n\nThe size recommendation engine\n\nThe brief called for \"AI-based size recommendations.\" Rather than reaching for a trained ML model on day one (which needs a lot of labeled fit data most fashion startups don't have yet), I built a rule-based measurement-matching engine instead — one that's explainable, debuggable, and immediately useful with just a size chart.\n\nHere's the core idea: each product category has a SizeChart with min/max chest, waist, hip, and height ranges per size. When a user submits their measurements, the engine scores every size in the chart:\n\nA perfect fit inside the range scores 1.0\n\nA near-miss just outside the range gets partial credit that decays with distance\n\nMissing measurements are skipped, not penalized\n\nThen it layers on two nice touches: a fit preference (snug/regular/loose) nudges the result up or down a size, and if the user tells us a previous size \"ran small,\" that feedback becomes a strong prior for the next recommendation.\n\nThe result isn't a black box — it comes back with a confidence score and a plain-language explanation (\"Your chest and waist measurements best matched size M\"). That transparency matters more than raw accuracy for a first version: customers trust a system they can question. And because it's isolated in one function, swapping in a real ML model later is a drop-in replacement, not a rewrite.\n\nCheckout and the payment gateway abstraction\n\nCheckout needed to feel realistic without wiring up real payment credentials for a demo project. The solution was a small PaymentGateway interface with a mock implementation underneath:\n\npython\n\nclass PaymentGateway(ABC):\n\n[@abstractmethod](https://dev.to/abstractmethod)\n\ndef charge(self, amount, method, token, order_number) -> PaymentResult:\n\n...\n\nCash-on-delivery orders confirm instantly. Card and UPI payments simulate authorization with realistic latency and even include a deterministic failure hook for testing declined payments. Because every router depends on the interface rather than a specific provider, adding a real Stripe or Razorpay integration later means writing one new class and flipping a config value — no changes to the checkout endpoint itself.\n\nThe checkout flow also had to guard against a subtle race condition: a user adds an item to their cart, someone else buys the last unit, and now the original user tries to check out. The endpoint re-validates stock for every line item at checkout time, not just when items were added to the cart, and only decrements stock after payment succeeds.\n\nAuth, and a real bug worth mentioning\n\nPassword hashing went through bcrypt via passlib initially — the standard, well-documented choice. But testing surfaced a real compatibility issue: newer versions of the bcrypt package dropped an internal attribute that older passlib versions probe for during version detection, causing spurious failures even though the underlying hashing worked fine. The fix was to call bcrypt directly instead of routing through passlib's compatibility layer — fewer moving parts, and one less place for library version drift to cause silent breakage. It's a good reminder that \"well-established library\" doesn't mean \"immune to version mismatches,\" and that testing the actual auth flow end-to-end (not just reading the docs) catches things a code review alone wouldn't.\n\nWhat's next\n\nA few things I'd tackle if this were headed to production rather than a portfolio piece: rate limiting on the login/register endpoints, moving product images to real object storage instead of placeholder URLs, and swapping the mock payment gateway for a real processor. The architecture is set up to make all three straightforward additions rather than rewrites — which, in the end, was the real goal of this project: not just shipping features, but shipping them in a shape that can grow.\n\nBuilt as part of a Python full-stack internship project. Full source, setup instructions, and API docs available in the project README.", "url": "https://wpnews.pro/news/fashion-e-commerce-store", "canonical_source": "https://dev.to/ajith_5906ae8402d31b2d6d8/fashion-e-commerce-store-48fa", "published_at": "2026-09-02 06:12:57+00:00", "updated_at": "2026-09-02 06:23:13.798725+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["FastAPI", "MySQL", "SQLAlchemy", "Pydantic", "Alembic", "Bootstrap"], "alternates": {"html": "https://wpnews.pro/news/fashion-e-commerce-store", "markdown": "https://wpnews.pro/news/fashion-e-commerce-store.md", "text": "https://wpnews.pro/news/fashion-e-commerce-store.txt", "jsonld": "https://wpnews.pro/news/fashion-e-commerce-store.jsonld"}}