Production-grade Django + DRF backend for a multi-vendor B2B + B2C marketplace.
Key constraints implemented in this codebase:
- Manual API routing (no ViewSets)
- Services-layer business logic (serializers validate only)
- Redis-backed idempotency for write requests via
Idempotency-Key
- Django, DRF, SimpleJWT
- PostgreSQL
- Redis (cache + idempotency)
- Celery (workers + beat)
- Nginx + Certbot
- Gunicorn with Uvicorn workers (ASGI)
- Create a local env file
Use .env.example as a starting point:
cp .env.example .env- Start services
docker compose up --buildBackend:
- API:
http://localhost:8000/ - Swagger UI:
http://localhost:8000/docs/ - OpenAPI schema:
http://localhost:8000/schema/
Notes:
- The container entrypoint runs
python manage.py migrate --noinputon startup. .envmust never be committed (it is gitignored).
This backend uses JWT Bearer authentication.
-
POST /api/auth/register/returns:userobjecttokens.access+tokens.refresh
-
POST /api/auth/login/returns:access+refresh
-
POST /api/auth/refresh/expects{ "refresh": "..." }and returns a new access token payload.
Send:
Authorization: Bearer <access_token>
Recommended frontend approach:
- Store
accessin memory (or short-lived storage) andrefreshin secure storage. - On 401, call
/api/auth/refresh/, updateaccess, retry the original request.
For any write endpoint (POST/PUT/PATCH/DELETE), you can pass:
Idempotency-Key: <unique-key>
Behavior:
- Replaying the same key with the same payload returns the cached response.
- Replaying the same key with a different payload returns
409.
Frontend guidance:
- Always send
Idempotency-Keyfor:POST /api/orders/create/POST /api/payments/initiate/- refund / settlement-request flows
- Generate a UUID per “user action” and reuse it only when retrying the same action.
There are typically three frontend clients:
- Customer app (mobile/web)
- Seller panel
- Admin dashboard
All of them share the same API base:
{API_BASE_URL}/api/...
Core endpoints:
- Auth
POST /api/auth/register/POST /api/auth/login/POST /api/auth/logout/POST /api/auth/refresh/GET /api/auth/profile/PUT /api/auth/profile/update/
- Catalog
GET /api/categories/list/GET /api/products/list/GET /api/products/detail/GET /api/products/search/GET /api/products/filter/
- Cart
POST /api/cart/add/POST /api/cart/remove/POST /api/cart/update/GET /api/cart/get/POST /api/cart/clear/
- Checkout / Orders / Payments
POST /api/orders/create/(sendIdempotency-Key)GET /api/orders/list/GET /api/orders/detail/GET /api/orders/status/POST /api/payments/initiate/(sendIdempotency-Key)GET /api/payments/status/GET /api/payments/history/
- Notifications (in-app inbox)
GET /api/notifications/list/GET /api/notifications/unread-count/POST /api/notifications/mark-read/POST /api/notifications/mark-all-read/
Core endpoints:
- Seller onboarding
POST /api/sellers/register/POST /api/sellers/kyc/upload/(multipart upload)GET /api/sellers/profile/PUT /api/sellers/update/
- Seller operations
GET /api/sellers/orders/GET /api/sellers/orders/detail/POST /api/sellers/orders/update-status/(sendIdempotency-Key)GET /api/sellers/products/
- Seller analytics
GET /api/sellers/analytics/overview/GET /api/sellers/analytics/revenue/GET /api/sellers/analytics/orders/GET /api/sellers/analytics/top-products/
You have two admin surfaces:
- Django Admin UI:
GET /admin/
- Admin APIs (for a custom admin dashboard):
- Users
GET /api/admin/users/GET /api/admin/users/detail/POST /api/admin/users/block/POST /api/admin/users/unblock/
- Sellers
GET /api/admin/sellers/GET /api/admin/sellers/detail/POST /api/admin/sellers/approve/POST /api/admin/sellers/reject/
- Products moderation
GET /api/admin/products/GET /api/admin/products/detail/POST /api/admin/products/approve/POST /api/admin/products/reject/
- Orders
GET /api/admin/orders/GET /api/admin/orders/detail/
- Refunds / Commission
POST /api/admin/refund/(sendIdempotency-Key)POST /api/admin/commission/set/GET /api/admin/commission/get/
- Reports / Logs
GET /api/admin/reports/GET /api/admin/logs/
- Admin analytics
GET /api/admin/analytics/overview/GET /api/admin/analytics/revenue/GET /api/admin/analytics/users/GET /api/admin/analytics/orders/GET /api/admin/analytics/top-products/GET /api/admin/analytics/gmv/
Additional internal admin routes also exist:
GET /api/admin/catalog/products/pending/POST /api/admin/catalog/products/set-approval/GET /api/admin/commerce/settlements/list/GET /api/admin/commerce/settlements/detail/POST /api/admin/commerce/settlements/process/
These endpoints accept multipart/form-data:
POST /api/products/upload-image/POST /api/reviews/upload-media/POST /api/sellers/kyc/upload/
Use your HTTP client’s multipart support and refer to Swagger (/docs/) for the exact field names.
GET /api/health/→{ "status": "ok" }
POST /api/payments/webhook/is called by the payment provider (Razorpay). Do not call it from the frontend.
- Use a strong
SECRET_KEYand run withDEBUG=False. - Behind Nginx, the app is configured to respect
X-Forwarded-Proto. - If you enable HSTS preload/include-subdomains, do it deliberately (misconfiguration can lock you into HTTPS).