This project is a **multi-vendor e-commerce marketplace** (inspired by Taraj) that enables multiple sellers to list and sell products through a unified storefront, while administrators maintain full oversight through a private dashboard. Th
Master briefing file with problem statement, dev commands, and architecture principles.
This project is a multi-vendor e-commerce marketplace (inspired by Taraj) that enables multiple sellers to list and sell products through a unified storefront, while administrators maintain full oversight through a private dashboard. The platform solves three core problems:
Key domain concepts:
| Concept | Description |
|---|---|
| User | A registered person who can hold the role of customer, seller, or admin. A user has exactly one role assignment. |
| Seller Profile | A business entity linked to a user (role: seller). Contains business name, slug, description, logo, verification status, and documents. |
| Seller Application | A request submitted by a customer-role user to become a seller. Goes through an approval workflow reviewed by admin. |
| Product | A listed item owned by a specific seller. Has variants, pricing, stock, and category associations. |
| Order | A transaction linking a customer to one or more products from a single seller, with lifecycle tracking (pending → paid → fulfilled → shipped → delivered → cancelled). |
| Admin Dashboard | A protected Next.js route tree (/admin/*) exclusively accessible to users with the admin role. |
| Role | Entry Point | Core Actions |
|---|---|---|
| Customer | Registration / Sign-in | Browse catalog, search & filter products, add to cart, place orders, track order status, leave reviews, manage profile, apply to become a seller |
| Seller | Approval of seller application | Manage product CRUD, view and fulfill orders, manage inventory, view sales analytics, update seller profile |
| Admin | Admin login (/admin/signin) | Manage all users & sellers, approve/reject seller applications, moderate listings & reviews, view platform-wide analytics, manage categories, configure marketplace settings |
| Category | Metric | Target |
|---|---|---|
| Growth | Monthly Registered Users | +15% MoM |
| Growth | Active Sellers (products listed in last 30 days) | +10% MoM |
| Conversion | Cart-to-Order Conversion Rate | ≥ 3.5% |
| Conversion | Seller Application Approval Rate | Track & report (target: 80% of complete/valid apps) |
| Revenue | Gross Merchandise Value (GMV) | Monthly tracking |
| Revenue | Stripe Payment Success Rate | ≥ 98% |
| Performance | API p95 Latency | < 200ms |
| Performance | Page Load (LCP) | < 2.5s |
| Reliability | Order Processing Success Rate | ≥ 99.5% |
| Engagement | Repeat Purchase Rate (customers with ≥2 orders) | Track monthly |
| Layer | Technology | Rationale |
|---|---|---|
| Frontend & Admin UI | Next.js 16 (App Router) | Server Components reduce client JS; layout-based routing naturally models the admin/storefront separation; built-in caching and streaming for performance. Single codebase for customer storefront and admin dashboard. |
| Styling | Tailwind CSS v4 | Utility-first, zero-runtime CSS via the new CSS-first configuration (@import "tailwindcss"). Seamless integration with Next.js. Design-token-friendly and highly maintainable. |
| API Server / Webhooks | Express 5 | Dedicated API layer for Stripe webhooks, background-safe API routes, file upload processing, and any endpoints that don't belong in Next.js route handlers. Separation of concerns: Next.js handles UI-driven requests; Express handles integration/async concerns. |
| Database | PostgreSQL (via Supabase) | Mature relational database with strong ACID guarantees — essential for orders, inventory, and financial data. Supabase provides managed PostgreSQL, real-time subscriptions, and storage. |
| ORM | Drizzle ORM | Type-safe, SQL-like syntax that stays close to PostgreSQL. Zero overhead, excellent migration support, and first-class TypeScript generics. Avoids the abstraction leaks of heavier ORMs. |
| Authentication | Secure Session Auth (custom) | Server-side sessions stored in PostgreSQL with httpOnly cookies. Chosen over JWT for revocability and security. Integrated with Supabase Auth for initial sign-in (magic link + OAuth), then transitioned to our own session model for fine-grained role-based access control (RBAC). |
| Payments | Stripe | Industry-standard payment processing. Checkout Sessions for one-time purchases, Customer objects for saved payment methods. Webhooks processed via Express 5 with signature verification. |
| File Storage | Supabase Storage | Product images, seller logos, and verification documents. Signed URLs for access control. |
| Real-time | Supabase Realtime | Order status updates, live seller activity — pushed to clients via Postgres Change Data Capture (CDC). |
| Language | TypeScript (strict mode) | End-to-end type safety across Next.js, Express, Drizzle, and shared packages. |
| Package Manager | pnpm | Fast, disk-efficient monorepo package management with strict dependency resolution. |
| Validation | Zod | Runtime validation that doubles as static type inference. Used for API input validation, environment parsing, and form handling. |
| Resend | Transactional emails (order confirmations, seller application notifications, password reset). |
┌──────────────────────────────────────────────────────────────────┐
│ CLIENT (Browser) │
│ Next.js 16 App Router — Server Components + Client Components │
│ Tailwind CSS v4 — UI Layer │
└──────────┬───────────────────────────────────────────┬───────────┘
│ (UI-driven requests, SSR) │ (WebSocket/Realtime)
▼ ▼
┌──────────┴──────────────────┐ ┌──────────────────────────────┐
│ Next.js 16 (apps/web) │ │ Supabase Realtime │
│ ├─ /shop/* (Customer UI) │ │ (Order updates, notifications)│
│ ├─ /seller/* (Seller UI) │ └──────────────────────────────┘
│ └─ /admin/* (Admin UI) │
│ ├─ Server Actions │
│ ├─ Route Handlers (API) │
│ └─ Middleware (RBAC) │
└──────────┬──────────────────┘
│ (Server Actions → DB, API calls)
▼
┌──────────┴──────────────────────────────────────────────────────┐
│ DATABASE LAYER │
│ PostgreSQL (via Supabase) — Drizzle ORM │
│ ┌─────────┐ ┌──────────┐ ┌─────────┐ ┌──────────┐ ┌────────┐ │
│ │ users │ │ sellers │ │ products│ │ orders │ │ reviews│ │
│ └─────────┘ └──────────┘ └─────────┘ └──────────┘ └────────┘ │
│ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ ┌──────────┐ │
│ │ seller_apps │ │ categories │ │ order_items│ │ sessions │ │
│ └──────────────┘ └──────────────┘ └────────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────┘
│ (Webhooks, async processing)
▼
┌─────────────────────────────────────────────────────────────────┐
│ Express 5 (apps/api) │
│ ├─ Stripe Webhook Handler (signature verification) │
│ ├─ File Upload Processing │
│ ├─ Background-safe API endpoints │
│ └─ Health check / internal endpoints │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ EXTERNAL SERVICES │
│ Stripe (Payments) · Resend (Email) · Supabase Storage (Files) │
└─────────────────────────────────────────────────────────────────┘
| Decision | Rationale | Trade-off |
|---|---|---|
| Next.js + Express (not just Next.js Route Handlers) | Stripe webhooks require raw body access, specific headers, and retry logic better suited to Express. Separating them keeps concerns clean. | Adds an additional service to deploy and monitor. |
| Drizzle over Prisma | Drizzle generates plain SQL, giving full control over query optimization. Drizzle migrations are SQL-native and version-controlled. | Less "batteries-included" — no built-in schema migration UI (vs Prisma Studio). |
| Custom Session Auth over NextAuth.js | Full control over session lifecycle, RBAC, and session storage in PostgreSQL. Enables fine-grained permission checks per role. | More initial implementation effort; must handle session expiry, rotation, and revocation manually. |
| Supabase Auth for initial sign-in, then custom sessions | Leverages Supabase's robust OAuth/magic-link auth for onboarding, then transitions to our session model for RBAC. | Requires a session handoff flow on login. |
| Monorepo (pnpm workspaces) | Shared types, validation schemas, and DB client across Next.js and Express prevent drift. Single tsconfig base. | Slightly more complex CI/CD pipeline. |
| Tailwind CSS v4 (CSS-first config) | New @import "tailwindcss" approach in a single CSS file — no tailwind.config.js. Faster builds, design-token-native. | v4 is newer; some third-party plugin compatibility may require workarounds. |
# Clone the repository
git clone https://github.com/YOUR_ORG/multivendor-marketplace.git
cd multivendor-marketplace
# Install all dependencies (monorepo)
pnpm install
# Set up environment variables
cp .env.example .env.local
Create .env.local from the example below. All values must be set before running the application.
# ============================================
# DATABASE (Supabase PostgreSQL)
# ============================================
# Connection string for the PostgreSQL database managed by Supabase.
# Obtain from: Supabase Dashboard → Project Settings → Database
DATABASE_URL="postgresql://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT].supabase.co:5432/postgres"
# Supabase URL and keys (used for Auth, Storage, Realtime)
NEXT_PUBLIC_SUPABASE_URL="https://[YOUR-PROJECT].supabase.co"
SUPABASE_SERVICE_ROLE_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.[YOUR-KEY]"
NEXT_PUBLIC_SUPABASE_ANON_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.[YOUR-KEY]"
# ============================================
# NEXT.JS APPLICATION
# ============================================
# Internal API URL (Express server — used by Server Actions and Route Handlers)
API_URL="http://localhost:3001"
# Next.js session secret (min 32 chars, generate with: openssl rand -hex 32)
SESSION_SECRET="[GENERATE-32-CHAR-HEX-STRING]"
# Next.js public URL (used for absolute URLs in emails, webhooks)
NEXT_PUBLIC_APP_URL="http://localhost:3000"
# ============================================
# AUTH (Secure Session)
# ============================================
# Session cookie name (default: marketplace_session)
SESSION_COOKIE_NAME="marketplace_session"
# Session max age in milliseconds (default: 30 days)
SESSION_MAX_AGE_MS="2592000000"
# ============================================
# STRIPE
# ============================================
STRIPE_SECRET_KEY="sk_test_[YOUR-KEY]"
STRIPE_WEBHOOK_SECRET="whsec_[YOUR-KEY]"
# Public key is exposed to the browser via NEXT_PUBLIC_ prefix
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="pk_test_[YOUR-KEY]"
# Stripe webhook endpoint URL (for production): https://api.yourdomain.com/webhooks/stripe
STRIPE_WEBHOOK_ENDPOINT_URL="http://localhost:3001/webhooks/stripe"
# ============================================
# EMAIL (Resend)
# ============================================
RESEND_API_KEY="re_[YOUR-KEY]"
EMAIL_FROM_NAME="Marketplace"
EMAIL_FROM_ADDRESS="noreply@marketplace.com"
# ============================================
# SUP