Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ALEJ4NDRO2025/urban-store/llms.txt

Use this file to discover all available pages before exploring further.

Urban Store is a three-tier, containerised application. The Next.js frontend handles all user interaction and rendering, the Django REST Framework backend owns business logic and data access, and MongoDB Atlas stores all domain data. The three tiers are wired together by a single docker-compose.yml and communicate over Docker’s internal bridge network — no external service mesh or API gateway is required for local development.

System Overview

Three Docker services are defined in docker-compose.yml:
ServiceHost portImage / build contextDepends on
frontend3000./frontend (Next.js 16)backend
backend8000./backend (Django 6)mongo
mongo27017mongo:7 (official)
A named Docker volume (mongo_data) persists MongoDB data across container restarts. Both frontend and backend bind-mount their source directories into the container, so code changes are reflected without rebuilding the image during development.

Architecture Diagram

  Browser

     │  HTTP / HTTPS

┌─────────────────────────────────┐
│        Next.js 16 Frontend      │  :3000
│  App Router · Zustand · GSAP    │
│  Three.js · Recharts · Stripe.js│
│  Middleware (JWT route guard)   │
└────────────────┬────────────────┘
                 │  REST API calls (fetch)
                 │  Authorization: Bearer <JWT>

┌─────────────────────────────────┐
│     Django 6 + DRF Backend      │  :8000
│  /api/users/   /api/products/   │
│  /api/cart/    /api/orders/     │
│  /api/payments/ /api/analytics/ │
│  Auth: PyJWT + bcrypt (custom)  │
└──────┬──────────────────┬───────┘
       │ MongoEngine       │ External HTTP
       ▼                   ▼
┌────────────┐   ┌─────────────────────────┐
│  MongoDB   │   │   Third-Party Services  │
│  Atlas     │   │  • Cloudinary (images)  │
│  :27017    │   │  • Stripe (payments)    │
│  urbanstore│   │  • Gmail SMTP (email)   │
└────────────┘   └─────────────────────────┘

Frontend

The frontend is a Next.js 16 application using the App Router. All pages live under frontend/app/ and map directly to URL paths.

Pages

RoutePurpose
/Landing / home page
/catalogProduct listing and search
/carritoShopping cart
/checkoutShipping address collection
/paymentStripe payment form
/order-confirmationPost-purchase confirmation
/perfilUser profile
/adminAdmin dashboard (products, orders, analytics)
/loginAuthentication
/registerAccount creation
/verify-email6-digit email verification
/forgot-passwordPassword reset request
/reset-passwordPassword reset confirmation

Key Libraries

LibraryVersionRole
next^16.2.3Framework and routing
zustand^5.0.12Client-side cart and auth state
gsap^3.15.0UI animations and transitions
three^0.184.03D visual effects
three-stdlib^2.36.1Three.js utility extras
recharts^3.8.1Analytics charts in the admin dashboard
@stripe/react-stripe-js + @stripe/stripe-js^6.2.0 / ^9.2.0Stripe payment element
react-hot-toast^2.6.0In-app notifications
tailwindcss^3.4.1Utility-first styling

Middleware Route Guard

frontend/middleware.js intercepts every page request (excluding static files, images, and Next.js internals) before it reaches a React component. The middleware reads the access cookie to determine authentication state and enforces three rules:
  1. Public paths (/, /catalog, /login, /register, /verify-email, /forgot-password, /reset-password) are always accessible without a token.
  2. Auth paths (/login, /register) redirect already-authenticated users to /catalog to prevent re-login.
  3. Protected paths (everything else) redirect unauthenticated users to /login?redirect=<original-path> so they can return after logging in.
  4. /admin paths additionally decode the JWT payload and check the is_admin boolean claim. Non-admin users are redirected to /; users with a malformed token are sent to /login.
// Excerpt from frontend/middleware.js
if (pathname.startsWith('/admin') && token) {
  try {
    const payload = JSON.parse(atob(token.split('.')[1]))
    if (!payload.is_admin) {
      return NextResponse.redirect(new URL('/', request.url))
    }
  } catch (error) {
    return NextResponse.redirect(new URL('/login', request.url))
  }
}

Backend

The backend is a Django 6.0.3 project using Django REST Framework 3.16.1. All API endpoints are prefixed with /api/ and routed through config/urls.py.

URL Structure

# backend/config/urls.py
urlpatterns = [
    path('admin/',          admin.site.urls),
    path('api/users/',      include('users.urls')),
    path('api/products/',   include('products.urls')),
    path('api/cart/',       include('cart.urls')),
    path('api/orders/',     include('orders.urls')),
    path('api/payments/',   include('payments.urls')),
    path('api/analytics/',  include('analytics.urls')),
]

Installed Apps

Urban Store defines eight project-specific Django apps in INSTALLED_APPS:
AppStatusResponsibility
users✅ ActiveRegistration, login, email verification, password reset
products✅ ActiveProduct CRUD, Cloudinary image upload
orders✅ ActiveOrder creation, status lifecycle, stock management
cart✅ ActiveServer-side cart persistence and sync
payments✅ ActiveStripe PaymentIntent creation and confirmation
analytics✅ ActiveEvent tracking, dashboard data aggregation
chatbot🚧 StubDeclared in INSTALLED_APPS, not yet implemented
recomendaciones🚧 StubDeclared in INSTALLED_APPS, not yet implemented
The chatbot and recomendaciones apps are present in INSTALLED_APPS and have their own directories in the repository, but they do not yet expose any URL routes or meaningful views. They are placeholders for planned features and can be safely ignored during development.

Custom JWT Authentication

Urban Store configures rest_framework_simplejwt as the default DRF authenticator in REST_FRAMEWORK settings — djangorestframework_simplejwt==5.5.1 is installed and wired up via 'rest_framework_simplejwt.authentication.JWTAuthentication'. However, the project’s individual API views override this by explicitly setting:
authentication_classes = []
permission_classes = []
Authentication is handled manually using PyJWT (PyJWT==2.12.1) and bcrypt (bcrypt==4.0.1). Each protected view extracts and validates the token itself:
def get_payload(request):
    auth = request.headers.get('Authorization', '')
    if not auth.startswith('Bearer '):
        return None
    try:
        token = auth.split(' ')[1]
        return jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
    except:
        return None
Tokens are issued on login with an email and is_admin claim. The is_admin claim is the single source of truth for admin access on both the backend (checked in view logic) and the frontend (decoded in the Next.js middleware).

CORS Configuration

CORS is handled by django-cors-headers. Only the Next.js dev server is whitelisted:
CORS_ALLOWED_ORIGINS = [
    "http://localhost:3000",
]
Update this list when deploying to a public domain.

Database

MongoDB Atlas (Primary Store)

All domain data is stored in MongoDB Atlas, connected via MongoEngine (mongoengine==0.29.3). The connection is established at Django startup in settings.py:
mongoengine.connect(
    db='urbanstore',
    host=config['MONGODB_URI'],
    tlsCAFile=certifi.where()   # TLS certificate pinning via certifi
)
MongoEngine provides a Django-ORM-like document model API on top of PyMongo. Each app defines its domain documents as MongoEngine Document subclasses (e.g., User, Product, Order, Cart, Event).

SQLite (Unused)

Django’s default DATABASES configuration points to a local db.sqlite3 file. This is present to satisfy Django internals (session table, admin, etc.) but none of the Urban Store domain models use it. All meaningful data lives in MongoDB.

Third-Party Services

ServiceSDK / ProtocolPurpose
Cloudinarycloudinary==1.44.1 (Python SDK)Upload and serve product images via CDN
Stripestripe==15.0.1 (Python) + @stripe/react-stripe-js (JS)PaymentIntent creation (backend) and card UI (frontend)
Gmail SMTPDjango email backend (custom TLS context) over port 587Order confirmation emails, email verification codes, password reset links
MongoDB Atlasmongoengine==0.29.3 + pymongo==4.16.0Primary data store for all domain documents

Authentication Flow

The end-to-end authentication sequence for a new user:
1. POST /api/users/register/
   → bcrypt hashes the password
   → User document saved in MongoDB (is_verified: false)
   → 6-digit code generated and emailed via Gmail SMTP

2. POST /api/users/verify-email/  { code: "123456" }
   → Code and expiry checked against the User document
   → User document updated: is_verified: true

3. POST /api/users/login/
   → bcrypt.checkpw() validates the submitted password
   → jwt.encode() produces a signed HS256 token containing:
       { email, is_admin, exp }
   → Token returned in response body

4. Client stores the token in the `access` cookie

5. Subsequent requests include:
   Authorization: Bearer <token>

6. Each backend view calls get_payload(request) to decode
   and validate the token against SECRET_KEY

Data Flow: Purchase Journey

A complete purchase moves through the system in the following sequence:
User browses /catalog
  └─► GET /api/products/
        └─► MongoEngine queries MongoDB → product list returned

User adds item to cart
  └─► Cart state updated in Zustand (localStorage)
  └─► POST /api/cart/  (syncs with backend cart document)

User proceeds to checkout → enters shipping address
  └─► Shipping address held in client state

User submits order
  └─► POST /api/orders/
        ├─► JWT validated by get_user_id()
        ├─► Cart items read from backend
        ├─► Product stock decremented in MongoDB
        └─► Order document created (status: 'pending')

Stripe PaymentIntent created
  └─► POST /api/payments/create-intent/
        ├─► JWT validated
        ├─► Order total read from MongoDB
        └─► stripe.PaymentIntent.create() called
              └─► client_secret returned to frontend

User enters card details in Stripe Element
  └─► stripe.confirmPayment() called client-side
        └─► Stripe processes the card directly (PCI-compliant)

Payment confirmed
  └─► POST /api/payments/confirm/
        ├─► Order status updated to 'paid' in MongoDB
        ├─► paid_at timestamp recorded
        └─► Confirmation email sent via Gmail SMTP

User redirected to /order-confirmation
If a payment fails after stock has been decremented, the order remains in pending state. Implement a periodic cleanup job or Stripe webhook handler to restore stock for permanently failed orders before going to production.

Build docs developers (and LLMs) love