ClickCharm
Responsive e-commerce platform with OAuth 2.0, cart + checkout, and MySQL-backed REST APIs tuned for latency.
Problem
A ground-up e-commerce build to practice full CRUD, auth flows, and query tuning end-to-end — not another Stripe demo. Focus was on measurable API performance and a clean checkout path.
Impact
- 8+ REST endpoints across product, cart, and order flows
- 30% reduction in average API response time via query + schema tuning
- OAuth 2.0 user authentication end-to-end
Architecture
flowchart LR U[Browser] --> R[React SPA] R --> API[Express REST API] API --> Auth[OAuth 2.0 Provider] API --> DB[(MySQL)]
Mermaid flowchart source — renders as a diagram when embedded in docs.
Data model
- users (id, email, oauth_provider, created_at)
- products (id, name, price, stock, category_id)
- categories (id, name, slug)
- carts (id, user_id, updated_at)
- cart_items (id, cart_id, product_id, qty)
- orders (id, user_id, total, status, placed_at)
- order_items (id, order_id, product_id, qty, price)
API design
| Method | Path | Purpose |
|---|---|---|
| GET | /api/products | Paginated catalogue |
| GET | /api/products/:id | Product detail |
| POST | /api/cart/items | Add to cart |
| DELETE | /api/cart/items/:id | Remove from cart |
| POST | /api/orders | Place order (protected) |
| GET | /api/orders/me | Order history |
Features
- →OAuth 2.0 sign-in and protected routes
- →Product catalogue with categories and pagination
- →Cart with quantity updates and stock validation
- →Full checkout workflow ending in a persisted order
Engineering decisions & tradeoffs
MySQL over NoSQL
Orders are inherently relational: users → carts → items → orders. Normal forms + indexes on foreign keys were the fastest path to correctness and speed.
Measure, then optimize
The 30% latency win came from profiling first — the slow endpoints were doing N+1 lookups, not needing exotic caching.
Server-owned cart
Persisting the cart in MySQL rather than localStorage makes cross-device shopping work and simplifies stock checks at order time.
Challenges & lessons
- — Getting OAuth callbacks and JWT sessions to play nicely on the SPA without leaking tokens.
- — Making pagination and category filters use indexes instead of full scans.
Future improvements
- — Redis caching for hot product reads
- — Idempotency keys on order creation
- — Move to PostgreSQL for richer query features