Technology Stack
| Layer | Technology | Version/Details |
|---|---|---|
| Frontend Framework | Next.js | 15.0.0 |
| Frontend Styling | Tailwind CSS | 4.1.11 |
| Animations | Framer Motion | 12.38.0 |
| Backend API | Hono | 4.3.7 |
| Admin Framework | React + Vite | 18.3.1 |
| Admin UI | Shadcn/UI + Tailwind | 3.4.3 |
| Database | PostgreSQL | 16-alpine |
| ORM | Prisma | Latest |
| Cache & Queue | Redis + BullMQ | 7-alpine |
| Object Storage | MinIO | Latest |
| Authentication | JWT + bcryptjs | Latest |
| AI Integration | Google Gemini | 0.24.1 |
| Validation | Zod | 3.23.8 |
| Deployment | Docker + Traefik | Latest |
Purpose and Philosophy
Waffy (also referred to as Popular Enterprise) is a premium artisanal spice and pickle e-commerce platform designed to showcase and sell authentic Indian handcrafted pickles and pure spices. The project was born from the need to upgrade a legacy Express.js + EJS + SQLite application to a modern, scalable, enterprise-grade platform.
The core philosophy centers on delivering a sensory-rich user experience that reflects the premium nature of artisanal spices. The platform emphasizes heritage flavors, sustainable sourcing storytelling, and modern culinary applications. By building on a monorepo architecture, the project ensures type safety across all layers, shared business logic, and efficient development workflows.
Core Design Principles
- Monorepo Architecture: All applications (web, api, admin, worker) reside in a single repository with shared packages for types, database schemas, and utilities. This enables strict TypeScript consistency and easy refactoring.
- API-First Design: The frontend is completely decoupled from the backend. The Hono API serves as the single source of truth, enabling future mobile apps, third-party integrations, and AI agent access without duplicating business logic.
- Separation of Concerns: The backend follows a strict layered architecture with Controllers (HTTP handling), Services (business logic), and Repositories (data access). This ensures maintainability as the application grows.
- Type Safety End-to-End: Zod schemas define validation at both API boundaries (runtime) and TypeScript compile time. The same schemas are shared between frontend and backend via the
@popular/typespackage.
- Background Processing: Long-running operations like Telegram notifications, image optimization, and audit log writes are handled by a dedicated worker service using BullMQ, keeping API responses fast.
Architecture Deep Dive
Monorepo Structure
spices-pickle-website/
├── apps/
│ ├── web/ # Customer-facing Next.js 15 frontend
│ │ ├── app/ # App Router pages
│ │ │ ├── page.tsx # Homepage
│ │ │ ├── shop/ # Shop with filtering
│ │ │ ├── product/[slug]/ # Product detail page
│ │ │ ├── blog/ # Blog/recipe section
│ │ │ ├── flavor-lab/ # Interactive spice mixer
│ │ │ └── checkout/ # Quote checkout flow
│ │ ├── components/ # React components
│ │ ├── lib/ # Contexts, utilities, API client
│ │ └── public/ # Static assets
│ │
│ ├── api/ # Hono REST API
│ │ ├── src/
│ │ │ ├── routes/ # API endpoint definitions
│ │ │ ├── services/ # Business logic
│ │ │ ├── middleware/ # Auth, validation, rate-limiting
│ │ │ ├── lib/ # Prisma, Redis, MinIO clients
│ │ │ └── tests/ # Vitest unit tests
│ │ └── package.json
│ │
│ ├── admin/ # React + Vite Admin Dashboard
│ │ ├── src/
│ │ │ ├── pages/ # Dashboard, Products, Quotes, etc.
│ │ │ ├── components/ # Layout, Charts, UI components
│ │ │ └── lib/ # API client, auth store
│ │ ├── nginx.conf # Production nginx config
│ │ └── package.json
│ │
│ └── worker/ # Background job processor
│ ├── src/
│ │ ├── jobs/ # Telegram, image processing
│ │ └── lib/ # Redis, MinIO, logger
│ └── package.json
│
├── packages/
│ ├── database/ # Prisma schema + migrations
│ └── types/ # Shared TypeScript types + Zod schemas
│
├── docker-compose.yml # Full stack orchestration
├── pnpm-workspace.yaml # Monorepo configuration
└── project-overview.md # Architecture documentationAPI Layer (Hono)
The Hono API provides a comprehensive RESTful interface:
// apps/api/src/index.ts
const app = new Hono();
app.use("*", cors());
app.use("*", secureHeaders());
app.use("*", rateLimiter({ windowMs: 900000, max: 100 }));
app.get("/health", (c) => c.json({ status: "ok" }));
app.route("/api/v1", authRoutes);
app.route("/api/v1", productRoutes);
app.route("/api/v1", quoteRoutes);
app.route("/api/v1", categoryRoutes);
app.route("/api/v1", feedbackRoutes);
app.route("/api/v1", contactRoutes);
app.route("/api/v1", complianceRoutes);
app.route("/api/v1", blogRoutes);
app.route("/api/v1", aiRoutes);
app.route("/api/v1/admin", adminRoutes);Database Schema (Prisma)
The PostgreSQL schema models the entire e-commerce domain:
model Product {
id Int @id @default(autoincrement())
name String
slug String @unique
categoryId Int
category Category @relation(fields: [categoryId], references: [id])
description String
mainImage String?
gallery String[]
badge String?
origin String?
flavorProfile String[]
aromaticProfile Json?
isActive Boolean @default(true)
variants ProductVariant[]
quotes Quote[]
createdAt DateTime @default(now())
}
model ProductVariant {
id Int @id @default(autoincrement())
productId Int
product Product @relation(fields: [productId], references: [id])
size String
price Decimal @db.Decimal(10, 2)
costPrice Decimal? @db.Decimal(10, 2)
}
model Quote {
id Int @id @default(autoincrement())
name String
phone String
email String?
productId Int
product Product @relation(fields: [productId], references: [id])
variantId Int
variant ProductVariant @relation(fields: [variantId], references: [id])
quantity Int
address String?
message String?
status QuoteStatus @default(NEW)
logs QuoteLog[]
createdAt DateTime @default(now())
}
enum QuoteStatus {
NEW
CONTACTED
NEGOTIATING
COMPLETED
CANCELLED
}Authentication System
JWT-based authentication with token rotation:
- Access Token: 15-minute TTL, sent in Authorization header
- Refresh Token: 7-day TTL, stored in HTTP-only cookie
- Password Hashing: bcrypt with cost factor 12
- Role-Based Access: SUPER_ADMIN, ADMIN, VIEWER roles
// Token payload structure
{
sub: adminId, // admin user ID
role: "ADMIN", // role from AdminRole enum
iat: timestamp,
exp: now + 15min // short-lived for security
}Key Features and Functionality
1. Customer Frontend (Next.js 15)
The customer-facing website includes:
Pages:
- Homepage (
/): Hero section, featured products, best sellers, categories grid, testimonials, blog section, newsletter signup - Shop (
/shop): Full product catalog with filtering (category, price, badge), search, sorting (price/name/newest), grid/list toggle - Product Detail (
/product/[slug]): Image gallery, aromatic radar chart, flavor profile visualization, detailed descriptions, variant selection - Blog (
/blog): Recipe-style articles with ingredients and instructions - Flavor Lab (
/flavor-lab): Interactive spice mixing tool (VisualMixer) - Checkout (
/checkout): Quote request form
Key Components:
NavBar.tsx: Full navigation with cart, wishlist, searchHero.tsx: Animated landing hero with glassmorphismProductDetailsClient.tsx: Full PDP with all interactionsFlavorMap.tsx: D3-based geographic origin mapRadarChart.tsx: Aromatic profile visualizationVisualMixer.tsx: Interactive spice blending toolCulinaryAssistant.tsx: AI-powered cooking assistant (Gemini)SpiceSommelier.tsx: AI spice recommendation engineMolecularPairing.tsx: Ingredient pairing engine
State Management:
- Cart via React Context (
lib/cart-context.tsx) - Wishlist via React Context (
lib/wishlist-context.tsx)
2. Admin Dashboard (React + Vite)
The admin panel provides complete business management:
Pages:
- Dashboard: KPI cards, sales charts, recent quotes
- Products: CRUD with variants, image upload
- Categories: Category management
- Quotes: Quote list, status updates, follow-up notes
- Sales: Sales records, conversion from quotes
- Feedbacks: Approve/reject customer reviews
- Contacts: View and reply to contact queries
- Compliances: Upload/delete compliance certificates
- Audit Logs: Read-only activity trail
- Settings: Site-wide configuration (phone, email, Telegram)
- Reports: Sales by product, quote conversion funnel
Stack:
- Vite + React 18 + TypeScript
- TanStack Query for API data fetching
- Zustand for auth state
- Shadcn/UI components
- Recharts for visualizations
3. API Endpoints
Public Endpoints:
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/products | List products with filters |
| GET | /api/v1/products/:slug | Get single product |
| GET | /api/v1/categories | List all categories |
| POST | /api/v1/quotes | Submit quote request |
| GET | /api/v1/feedbacks | Get approved feedbacks |
| POST | /api/v1/feedbacks | Submit feedback |
| POST | /api/v1/contacts | Submit contact message |
Admin Endpoints (JWT Protected):
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/admin/auth/login | Admin login |
| POST | /api/v1/admin/auth/refresh | Refresh token |
| GET/POST/PUT/DELETE | /api/v1/admin/products | Product CRUD |
| GET/PUT | /api/v1/admin/quotes | Manage quotes |
| GET | /api/v1/admin/audit-logs | View audit trail |
4. Background Worker
BullMQ-powered worker handles:
- Telegram Notifications: Real-time alerts for new quotes, contacts, quote status changes
- Image Optimization: Sharp-based resizing to WebP format
- Analytics Events: Asynchronous event processing
// Queue setup
export const notificationQueue = new Queue("notifications", { connection: redis });
export const imageQueue = new Queue("image-processing", { connection: redis });5. Caching Strategy
Redis caching with strategic TTLs:
| Data | Cache Key Pattern | TTL |
|---|---|---|
| All categories | categories:all | 1 hour |
| Products list | products:list:{hash} | 30 min |
| Single product | product:{slug} | 1 hour |
| Approved feedbacks | feedbacks:approved | 2 hours |
| Admin KPIs | admin:kpis | 5 min |
6. File Storage (MinIO)
S3-compatible object storage for:
- Product images (main + gallery)
- Compliance documents
- Admin uploads
// Bucket structure
popular-uploads/
├── products/{productSlug}/
│ ├── main.webp
│ └── gallery-1.webp
├── compliances/
│ └── fssai-license.pdf
└── misc/
└── admin-uploads/Deployment and Infrastructure
Docker Compose Services
The production stack runs seven containers:
services:
web: # Next.js frontend (port 3013 → 3011)
api: # Hono API (port 3012 → 3010)
admin: # React admin (port 3014 → 80)
postgres: # PostgreSQL 16 (port 5435)
redis: # Redis 7 (port 6381)
minio: # MinIO object storage (ports 9006, 9016)
worker: # Background job processorTraefik Routing
Domain routing configuration:
popular.bajali.in→ Web frontendapi-popular.swaraniretreat.in→ APIadmin-popular.swaraniretreat.in→ Admin dashboardminio-popular.swaraniretreat.in→ MinIO console
All routes use HTTPS with Let's Encrypt certificates.
Security Implementation
Authentication & Authorization
- JWT access tokens (15-minute TTL)
- Refresh token rotation (7-day TTL)
- bcrypt password hashing (cost factor 12)
- Role-based access control (SUPER_ADMIN, ADMIN, VIEWER)
API Security
- Rate limiting (100 requests per 15 minutes)
- CORS whitelist for allowed origins
- CSRF protection middleware
- Secure headers (HSTS, X-Frame-Options, etc.)
- Brute force protection on login endpoints
- Zod validation on all inputs
Infrastructure Security
- Network isolation via Docker networks
- Traefik dashboard behind basic auth
- Environment variables for all secrets
- HTTP-only cookies for refresh tokens
Lessons Learned and Best Practices
What Worked Well
- Monorepo Structure: Shared
@popular/typespackage ensures frontend and backend type consistency - Hono Framework: Clean middleware composition, edge-ready, excellent TypeScript support
- Prisma ORM: Type-safe queries, easy migrations, excellent PostgreSQL support
- BullMQ Workers: Reliable background job processing, automatic retries
- Zod Validation: Runtime + compile-time validation, shared schemas between frontend/backend
Areas for Enhancement
- Testing Coverage: Unit tests exist but coverage could be expanded
- API Documentation: Swagger UI exists but could be more comprehensive
- Monitoring: No dedicated observability stack (Prometheus/Grafana)
- CI/CD: Deployment is manual, could benefit from GitHub Actions
Future Expansion
The architecture supports:
- Mobile Apps: Same API serves native mobile clients
- Multiple Brands: Multi-tenant architecture possible
- Subscription Model: Recurring billing integration
- Marketplace: Vendor management extension
Conclusion
Waffy represents a production-grade e-commerce platform built with modern best practices. The monorepo architecture, type-safe code, separated concerns, and containerized deployment provide a solid foundation for scaling. The rich frontend experience with AI-powered features like the Culinary Assistant and Molecular Pairing demonstrates innovative approaches to e-commerce beyond simple product listings.
The project successfully migrated from a legacy Express/SQLite monolith to a modern stack while maintaining the core business functionality and adding significant new capabilities for business growth.
Development Workflow and Commands
Setting Up Development Environment
The project uses pnpm workspaces for monorepo management. After cloning the repository:
# Install all dependencies
pnpm install
# Start all services in development mode
pnpm devThis launches all applications with hot reloading:
- Web frontend: http://localhost:3000
- API: http://localhost:3010
- Admin: http://localhost:5173
Building for Production
# Build all packages and apps
pnpm build
# Or build individual apps
cd apps/web && pnpm build
cd apps/api && pnpm build
cd apps/admin && pnpm buildDocker Deployment
The project includes comprehensive Docker configuration for production deployment:
# Build and start all services
docker-compose up -d
# View logs
docker-compose logs -f
# Rebuild specific service
docker-compose build web
docker-compose up -d --force-recreate webDatabase Operations
# Run Prisma migrations
pnpm prisma migrate deploy
# Generate Prisma client
pnpm prisma generate
# Seed database
pnpm prisma db seedKey Business Processes
Quote Workflow
The platform uses a quote-based workflow rather than direct purchases:
- Customer browses products and selects variants (size/quantity)
- Customer submits quote request with contact details
- Admin receives Telegram notification of new quote
- Admin reviews and updates quote status (CONTACTED, NEGOTIATING)
- On successful conversion, quote becomes sales record
- Customer is notified of status changes
Product Management
Admins can:
- Create products with multiple variants (different sizes/prices)
- Upload main image and gallery images (stored in MinIO)
- Set product badges (e.g., "Hot", "New", "Best Seller")
- Define aromatic profiles (spicy, earthy, sweet, floral, pungent)
- Specify geographic origin with coordinates for FlavorMap
- Toggle product visibility (active/inactive)
Content Management
The blog section serves as a recipe repository:
- Recipe-style articles with ingredient lists
- Step-by-step cooking instructions
- Integration with product catalog (link ingredients to products)
- SEO-optimized for discoverability
Performance Optimization
Frontend Optimizations
- Server-Side Rendering: Next.js App Router provides SSR for SEO
- Incremental Static Regeneration: Product pages rebuild every hour
- Image Optimization: WebP format via Sharp processing
- Bundle Analysis: Code splitting by route
Backend Optimizations
- Redis Caching: All product and category queries cached
- Database Indexing: Proper indexes on frequently queried columns
- Connection Pooling: Prisma manages PostgreSQL connection pool
- BullMQ Workers: Background processing keeps API fast
Infrastructure Optimizations
- Traefik: Efficient reverse proxy with automatic SSL
- MinIO: S3-compatible storage with CDN potential
- Docker: Efficient container resource allocation
Monitoring and Maintenance
Health Checks
# Check API health
curl https://api-popular.swaraniretreat.in/api/v1/health
# Check container status
docker psLog Monitoring
# API logs
docker logs -f popular-api
# Web logs
docker logs -f popular-web
# Worker logs
docker logs -f popular-workerBackup Strategy
- PostgreSQL: Nightly pg_dump to volume
- Redis: AOF persistence enabled
- MinIO: Bucket replication to external storage
- Application state: Environment variables versioned in private branch
Troubleshooting Common Issues
Container Won't Start
Check logs: docker-compose logs <service>
Common causes:
- Environment variables missing
- Port conflicts on host
- Volume permission issues
- Network not created
Database Connection Errors
Ensure:
- PostgreSQL container is running
- DATABASE_URL is correct
- Network allows container-to-container communication
SSL Certificate Issues
- Verify domain DNS points to server IP
- Check Let's Encrypt can reach port 80
- Review acme.json file permissions (must be 600)
Competitive Advantages
Over Legacy System
| Feature | Legacy | New System |
|---|---|---|
| Database | SQLite | PostgreSQL |
| API | None | Full REST API |
| Admin | EJS templates | React SPA |
| Caching | None | Redis |
| Background jobs | None | BullMQ |
| TypeScript | No | Full |
Unique Selling Points
- AI-Powered Features: Culinary Assistant, Molecular Pairing, Spice Sommelier
- Visual Product Discovery: FlavorMap, RadarChart, VisualMixer
- Sensory Storytelling: Aromatic profiles, geographic origins, health benefits
- Modern UX: Glassmorphism, smooth animations, mobile-first design
- Scalable Architecture: Microservices-ready, cloud-native
This platform demonstrates that artisanal food e-commerce can be both traditional in product offering and cutting-edge in technology implementation.
Architecture Feedback
Spotted a potential optimization or antipattern? Let me know.