Architecture and tools - youneshenniwrites/fastapi-next-ecommerce GitHub Wiki

Architecture and tools

This page explains implemented boundaries. The monitoring runbook records verified development signals and trace samples; the canonical plan owns remaining acceptance.

Components and responsibilities

Component Tools Responsibility
Storefront Next.js App Router, React, TypeScript, Tailwind v4, shadcn/ui Catalog, accounts, cart, checkout and order history; responsive state/error handling
API Python, FastAPI, Pydantic Request validation, authentication, authorization, product/cart operations, order placement and sandbox payment lifecycle
Persistence SQLAlchemy, PostgreSQL Store users, products, cart lines, orders and payment state; enforce database constraints
Schema changes Alembic Apply versioned database migrations
API contract OpenAPI 3.1, Swagger UI, ReDoc, openapi-typescript, openapi-fetch Describe endpoints, explore requests and generate frontend types
Local runtime Docker Compose, uv, npm Start services and install locked dependencies
Delivery GitHub Actions, Dependabot Run verification and propose dependency updates

The application follows a modular monolith approach. It has separate frontend and API processes, but the backend is one application with modules, not a fleet of microservices. This keeps transactions and development manageable at the project's current scale.

Catalog request flow

sequenceDiagram
    participant B as Browser
    participant N as Next.js server
    participant F as FastAPI
    participant D as PostgreSQL
    B->>N: Open or navigate the collection
    N->>F: GET product search page and category counts through typed API client
    F->>D: Filter, sort, count and page products with SQLAlchemy
    D-->>F: Matching page, total and category counts
    F-->>N: Bounded JSON with GBP price strings and categories
    N-->>B: Render the selected page and URL-owned controls
    Note over B: Submit search or navigate filters/pages to request the next server render

Next.js uses a server-side API_BASE_URL. Catalog requests use no-store caching and a timeout. The collection reads one page of 24 products by default, with search, stock, category, sort and page held in the URL. FastAPI and PostgreSQL filter and order before pagination; the browser does not filter a preloaded catalog. Product details read one product and its embedded category rather than the category list.

VIN-289, delivered in PR #307, adds GET /api/v1/products/search: FastAPI filters and sorts the complete catalog before returning a bounded page and matching total from one database snapshot. It defaults to 24 products and caps requests at 100. VIN-289 left list, detail and admin writes unchanged. Prices remain exact GBP strings. See the query contract for validation, literal search characters, stable ties and collation limits. The development API was verified on 5 October with six products. On 8 October 2026 VIN-292 imported the reviewed catalog into development at Alembic revision 0010: 60 products, pages of 24, and schema backup backup_vin292_20261008T183008Z. Production was not imported. VIN-287, delivered in PR #309, connects this endpoint to server-rendered pagination and URL-owned filters. VIN-290, merged in PR #316, adds one category per product, GET /api/v1/categories/, and a category filter on search. Product reads include category as slug and name. Create requires a stored slug and returns 422 without a valid one. Update may omit it. Search returns 404 for an unknown slug; create and update return 422. All is the omitted search parameter. The collection page loads the category list. The product page loads the product and shows the embedded category. The development storefront was checked: Lighting shows Task Light, the product page names that category, and an unknown slug says the category does not exist.

Authentication and data correctness

Passwords use Argon2 hashing, with legacy bcrypt verification and upgrade on login. Login returns a JWT bearer token. Protected endpoints check the active user; product writes require an active admin. Public registration creates customers only. Next.js provides registration/login screens and server-mediated registration, login, profile and logout handlers. The bearer token is stored in a host-only HttpOnly cookie; FastAPI remains authoritative for active-user checks and expiry. Registration/login screens are delivered (#25 / #64). Profile/account navigation and sign-out are delivered in #26 / #66. The complete account journey is verified on desktop and mobile (#27).

Prices use Decimal / NUMERIC(12,2) and serialize as exact two-place strings in GBP. Frontend money logic uses integer pennies. Database constraints and API validation protect catalog values; order totals are calculated by the backend from immutable price snapshots.

Baseline response hardening

The API and storefront implement explicit MIME-sniffing and referrer policies (nosniff and strict-origin-when-cross-origin). Existing MIME types, private sessions and immutable asset caching remain intact. Unexpected API errors preserve the default response and still reach server error monitoring.

Read the response-header contract and intentional framework/provider exceptions. VIN-248 records the baseline proof. PR #254 adds framing denial, application HSTS and a report-only nonce CSP. The initial hosted nonce mismatch was corrected in PR #255 and verified in development and production, including matching script nonces, fresh private/no-store HTML, immutable assets, navigation/docs and existing development Sentry transport. PR #256 promoted the tested policy to enforcement. Its exact merged release passed gated development/production delivery and hosted enforced-policy checks, including controlled rejection of an untrusted inline script. VIN-126 records completed acceptance. The policy runbook owns bounded exceptions and rollback, and the issue owns delivery evidence and acceptance.

Contract flow

flowchart LR
    M[FastAPI routes and Pydantic models] --> O[OpenAPI snapshot]
    O --> T[Generated TypeScript types]
    T --> C[Next.js API client]
    M --> S[Swagger UI and ReDoc]
    O --> P[Postman import]

CI regenerates the snapshot and types to detect drift. Type generation provides compile-time checks, not runtime response validation.

See current architecture and API contract.

Customer session boundary

PR #41 adds POST /api/session/login, GET /api/session/me and POST /api/session/logout. PR #64 adds POST /api/session/register and the /register and /login screens. Registration does not automatically sign in. A sign-in with no selected product returns to the catalog. Sign in to add returns to that product and keeps the recognised collection filters. Mutations require an exact configured trusted origin (APP_ORIGIN or a validated APP_ORIGIN_ALIASES entry); private responses are not cached. HTTPS uses Secure cookies, and loopback HTTP requires explicit local configuration. Logout clears the browser cookie but does not revoke a copied JWT. See the session design and configuration.

Hosted demo

GitHub owns code, reviews and delivery. Vercel Hobby hosts separate development and production projects for Next.js and FastAPI; Neon Free stores their data in separate PostgreSQL projects and roles. Each API has its own signing key, and each frontend uses the exact trusted HTTPS origin for session writes. No Render service or paid Azure infrastructure is used.

flowchart LR
    Visitor[Visitor] --> Web[Next.js on Vercel]
    Web --> API[FastAPI on Vercel]
    API --> DB[(Neon PostgreSQL)]
    Dev[Development frontend and API] --> DevDB[(Separate development database)]

The public endpoints are VINDOR and Swagger. The initial production release uses reviewed main commit 4606e67. Hosted PostgreSQL can suspend idle compute; the API validates pooled connections before checkout to replace closed connections. It does not retry interrupted transactions or replay writes. Free quotas and cold starts remain demo limitations.

Frontend design system

VINDOR uses Tailwind semantic tokens and editable shadcn components. Header/footer, section headings, product cards and details are shared compositions. Mobile navigation uses a Sheet with focus management; catalog tools use Input, Checkbox and shadcn Select. Lucide supplies consistent UI icons. Product/hero artwork was subsequently replaced with licensed local photography in #59. The initial migration was delivered in #56. #78 subsequently standardized control radii and keyboard focus, replaced native sorting with shadcn Select, and aligned catalog/product loading layouts. The homepage keeps its static hero visible while the catalog streams through a Suspense boundary. See the design-system handbook for the enforced rules and verified release evidence.

The component guide covers adding components with the pinned CLI, Tailwind Preflight after the completed page migration, server/client boundaries, accessibility and reduced motion. Frontend agent guidance points to the same rules.

Photography and identity

VINDOR uses locally served, licensed WebP photographs and responsive image sizes. A product stores the photograph's file key, so a rename keeps the picture. A missing or unknown key uses a Lucide placeholder. Photos are representative of the fictional collection; sources and usage context are recorded in the repository photo credits. Repository slug and database identifiers remain stable; original FORME domains remain aliases alongside the new VINDOR addresses. The public storefront, delivery board, Vercel projects and Neon project display names use VINDOR. Actual database names, roles and project IDs remain unchanged.

Account page and navigation

The /account page contains a public HTML shell; private email and membership date are fetched through the no-store session endpoint after load. Missing/expired sessions prompt sign-in; outages show a retry state. Hidden pages do not fetch profiles and discard existing snapshots. Visible pages revalidate on focus/restoration and every minute. Guest sign-in links use client navigation without prefetching. Authenticated account navigation and successful login/logout use full navigations to discard cached session UI. Logout clears the browser cookie without revoking copied JWTs. Tests cover these cases on desktop and mobile; see the session design.

Persistent cart API

Signed-in customers can read their saved cart, set absolute quantities and remove items through FastAPI. The database keys each line by customer and product; active-user authentication supplies ownership. Current backend prices determine exact GBP totals. Stock is not reserved: later shortages remain visible, reductions/removal remain allowed, and checkout must revalidate separately. PostgreSQL serializes customer cart writes and cascades deleted products out of carts.

The API is delivered in PR #79. Storefront controls are delivered in PR #80 (#72). See the versioned cart contract and transaction rules for endpoint examples and validation.

Observability

Development errors, traces, logs and metrics are verified under VIN-121, with joined trace proof in VIN-223 and safe labels/release/HTTP metadata in VIN-230/VIN-232. Missing DSNs keep telemetry silent; new signal channels require privacy tests before activation. Production activation is outside this rollout. Sampling, quota guardrails, and privacy scrubbing (auth headers, credential headers, sensitive body/query keys, user PII) are documented in the versioned runbook. The browser key is public by design; all other DSNs are server-only environment variables.

Rate-limit identity

Authenticated cart/product write limits use the verified active customer ID after PR #163 merged as b47be4a. Customers sharing frontend egress no longer share that write budget. Authentication/admin checks still apply. Counters remain per-process and resettable; this is not distributed protection.

VIN-158 / ISSUE #158 now has verified signed anonymous login identity, unchanged threshold separation and actual same-instance hosted proof with diagnostic cleanup. This is process-local protection, not a distributed budget. See the canonical plan for progress, not a separate Wiki checklist.

Order placement and payment boundary

VIN-118 stores owned draft orders with immutable GBP snapshots. VIN-119 (merged PR #181) revalidates cart, prices and stock, then atomically claims inventory, places the order and removes purchased cart lines. Customer-scoped idempotency supports retries after response loss. Placement is not payment. VIN-120 exposes checkout and order history in merged PR #188 (hosted checkout and history verified); VIN-30 adds hosted sandbox Checkout, verified webhooks, expiry/cancellation and exactly-once inventory release in PR #194, merged as 0e89efd. Development sandbox purchase, cancellation, expiry and replay evidence are verified; production payments remain off. See ADR 0002.