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:
| Service | Host port | Image / build context | Depends on |
|---|
frontend | 3000 | ./frontend (Next.js 16) | backend |
backend | 8000 | ./backend (Django 6) | mongo |
mongo | 27017 | mongo: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
| Route | Purpose |
|---|
/ | Landing / home page |
/catalog | Product listing and search |
/carrito | Shopping cart |
/checkout | Shipping address collection |
/payment | Stripe payment form |
/order-confirmation | Post-purchase confirmation |
/perfil | User profile |
/admin | Admin dashboard (products, orders, analytics) |
/login | Authentication |
/register | Account creation |
/verify-email | 6-digit email verification |
/forgot-password | Password reset request |
/reset-password | Password reset confirmation |
Key Libraries
| Library | Version | Role |
|---|
next | ^16.2.3 | Framework and routing |
zustand | ^5.0.12 | Client-side cart and auth state |
gsap | ^3.15.0 | UI animations and transitions |
three | ^0.184.0 | 3D visual effects |
three-stdlib | ^2.36.1 | Three.js utility extras |
recharts | ^3.8.1 | Analytics charts in the admin dashboard |
@stripe/react-stripe-js + @stripe/stripe-js | ^6.2.0 / ^9.2.0 | Stripe payment element |
react-hot-toast | ^2.6.0 | In-app notifications |
tailwindcss | ^3.4.1 | Utility-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:
- Public paths (
/, /catalog, /login, /register, /verify-email, /forgot-password, /reset-password) are always accessible without a token.
- Auth paths (
/login, /register) redirect already-authenticated users to /catalog to prevent re-login.
- Protected paths (everything else) redirect unauthenticated users to
/login?redirect=<original-path> so they can return after logging in.
/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:
| App | Status | Responsibility |
|---|
users | ✅ Active | Registration, login, email verification, password reset |
products | ✅ Active | Product CRUD, Cloudinary image upload |
orders | ✅ Active | Order creation, status lifecycle, stock management |
cart | ✅ Active | Server-side cart persistence and sync |
payments | ✅ Active | Stripe PaymentIntent creation and confirmation |
analytics | ✅ Active | Event tracking, dashboard data aggregation |
chatbot | 🚧 Stub | Declared in INSTALLED_APPS, not yet implemented |
recomendaciones | 🚧 Stub | Declared 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
| Service | SDK / Protocol | Purpose |
|---|
| Cloudinary | cloudinary==1.44.1 (Python SDK) | Upload and serve product images via CDN |
| Stripe | stripe==15.0.1 (Python) + @stripe/react-stripe-js (JS) | PaymentIntent creation (backend) and card UI (frontend) |
| Gmail SMTP | Django email backend (custom TLS context) over port 587 | Order confirmation emails, email verification codes, password reset links |
| MongoDB Atlas | mongoengine==0.29.3 + pymongo==4.16.0 | Primary 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.