A production-ready e-commerce backend API built with modern technologies, featuring JWT authentication, role-based access control, Redis caching, rate limiting, and comprehensive Swagger documentation.
- Features
- Tech Stack & Architecture Decisions
- Prerequisites
- Complete Setup Guide
- Environment Variables
- Database Management
- Creating Admin Users
- API Documentation
- Caching Strategy
- Rate Limiting
- Project Structure
- API Endpoints Reference
- Scripts Reference
- Submission & Deployment
- License
| Feature | Description |
|---|---|
| π JWT Authentication | Secure authentication with access + refresh token strategy |
| π₯ Role-Based Access Control | Admin and User roles with granular permissions |
| π¦ Product Management | Full CRUD with cursor-based pagination and advanced filtering |
| π Shopping Cart | Add, update, remove items with real-time stock validation |
| π Order Management | Create orders from cart, view order history |
| π³ Checkout Flow | Mock payment processing for demonstration |
| β‘ Redis Caching | Lightning-fast responses with intelligent cache invalidation |
| π‘οΈ Rate Limiting | Multi-tier request throttling for API protection |
| π API Documentation | Interactive Swagger/OpenAPI documentation |
NestJS was chosen as the framework for several compelling reasons:
- TypeScript-first: Full type safety out of the box, reducing runtime errors
- Modular Architecture: Clean separation of concerns with modules, controllers, and services
- Dependency Injection: Built-in DI container for testable, maintainable code
- Decorator-based: Familiar patterns for developers coming from Angular or Java/Spring
- Rich Ecosystem: First-class support for OpenAPI, validation, caching, and more
I chose Drizzle ORM over alternatives like Prisma or TypeORM for the following reasons:
| Aspect | Drizzle Advantage |
|---|---|
| Type Safety | SQL-like syntax with full TypeScript inference - you write SQL, get type safety |
| Performance | Lightweight with zero dependencies, minimal runtime overhead |
| Flexibility | Direct SQL when needed, no "magic" - what you write is what runs |
| Bundle Size | Significantly smaller than Prisma (~7KB vs ~300KB) |
| No Code Generation | Schema-as-code approach - no prisma generate step needed |
| Drizzle Studio | Built-in visual database browser (pnpm db:studio) |
- ACID Compliance: Reliable transactions for e-commerce operations
- JSON Support: Native JSONB for flexible data structures
- Scalability: Handles high-volume concurrent operations
- Ecosystem: Excellent tooling and community support
- Speed: In-memory data store with sub-millisecond latency
- Persistence: Optional data persistence for cache survival
- TTL Support: Automatic cache expiration management
- Production Ready: Battle-tested in high-traffic applications
Before you begin, ensure you have the following installed:
- Node.js v18.0.0 or higher (Download)
- pnpm v8.0.0 or higher (
npm install -g pnpm) - Docker & Docker Compose (Download)
- Git for version control
Follow these steps to get the project running locally:
git clone https://github.com/TheSamadAzeez/nexus-backend.git
cd nexus-backendpnpm installCreate your environment file from the template:
cp .env.example .envEdit the .env file with your preferred settings (see Environment Variables section).
This starts PostgreSQL and Redis containers:
docker-compose up -dVerify containers are running:
docker-compose psYou should see both nexus-postgres and nexus-redis with status Up.
Apply the database schema to PostgreSQL:
pnpm db:pushPopulate the database with sample data including test users and products:
pnpm db:seedThis creates:
- Admin User:
admin@nexus.com/admin123 - Test User:
user@nexus.com/user123 - 33 Sample Products across categories: Electronics, Footwear, Clothing, Accessories, Watches, Gaming
To create your own admin user account:
pnpm db:create-adminFor more info on creating an admin user, see the Creating Admin Users section below.
pnpm start:devThe API will be available at http://localhost:3000
Open your browser and navigate to:
http://localhost:3000/api
π You're all set! The Swagger UI provides interactive documentation for all endpoints.
Create a .env file in the project root with the following variables:
# Database Configuration
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_USER=admin
POSTGRES_PASSWORD=admin
POSTGRES_DB=nexus
# Redis Configuration
REDIS_HOST=localhost
REDIS_PORT=6379
# JWT Configuration
JWT_SECRET=your-super-secret-jwt-key-change-in-production
JWT_EXPIRES_IN=15m
JWT_REFRESH_SECRET=your-super-secret-refresh-key-change-in-production
JWT_REFRESH_EXPIRES_IN=7d
# Application Configuration
PORT=3000
NODE_ENV=development| Variable | Description | Default | Required |
|---|---|---|---|
POSTGRES_HOST |
PostgreSQL server hostname | localhost |
Yes |
POSTGRES_PORT |
PostgreSQL server port | 5432 |
Yes |
POSTGRES_USER |
Database username | admin |
Yes |
POSTGRES_PASSWORD |
Database password | admin |
Yes |
POSTGRES_DB |
Database name | nexus |
Yes |
REDIS_HOST |
Redis server hostname | localhost |
Yes |
REDIS_PORT |
Redis server port | 6379 |
Yes |
JWT_SECRET |
Secret key for access tokens | - | Yes |
JWT_EXPIRES_IN |
Access token expiration time | 15m |
Yes |
JWT_REFRESH_SECRET |
Secret key for refresh tokens | - | Yes |
JWT_REFRESH_EXPIRES_IN |
Refresh token expiration time | 7d |
Yes |
PORT |
Server port | 3000 |
No |
NODE_ENV |
Environment mode | development |
No |
β οΈ Security Note: Always use strong, unique secrets in production. Never commit.envfiles to version control.
Drizzle comes with a built-in visual database browser. To explore your data:
pnpm db:studioThis opens Drizzle Studio in your browser at https://local.drizzle.studio where you can:
- Browse all tables and their data
- Execute queries
- View and edit records
- Understand table relationships
The database consists of the following tables:
| Table | Description |
|---|---|
users |
User accounts with roles (ADMIN, USER) |
products |
Product catalog with categories |
carts |
User shopping carts |
cart_items |
Items in shopping carts |
orders |
Order records |
order_items |
Items in orders |
After modifying schemas in src/database/schemas/, apply changes:
pnpm db:pushTo reset and re-seed the database:
# Drop and recreate schema (use with caution!)
docker-compose down -v
docker-compose up -d
pnpm db:push
pnpm db:seed
β οΈ IMPORTANT: This is an interactive CLI command that runs in your terminal!
To create additional admin users, use the built-in CLI script:
pnpm db:create-adminThe script will interactively prompt you for:
π Create Admin User
========================================
Email: newadmin@example.com
Name: John Admin
Password (min 8 characters): ********
Confirm Password: ********
β³ Creating admin user...
β
Admin user created successfully!
========================================
ID: 550e8400-e29b-41d4-a716-446655440000
Email: newadmin@example.com
Name: John Admin
Role: ADMIN
========================================
π Done!
- Email: Must be a valid email format
- Name: Required, cannot be empty
- Password: Minimum 8 characters
- Confirmation: Passwords must match
- Creating initial admin accounts in production
- Adding new team members with admin privileges
- Recovering admin access without database manipulation
Once the server is running, comprehensive API documentation is available at:
http://localhost:3000/api
Features:
- Interactive endpoint testing
- Request/response schemas
- Authentication support (JWT Bearer)
- Model definitions
- Navigate to
http://localhost:3000/api - Click "Authorize" button
- Login via
/auth/loginendpoint to get a token - Paste the
accessTokenin the authorization field - Test authenticated endpoints
The API uses Redis as a caching layer via @nestjs/cache-manager with cache-manager-ioredis-yet.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Request Flow β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Request βββΊ Cache Check βββΊ Cache Hit? βββΊ Response β
β β β β
β β No β Yes β
β βΌ β β
β Database Query β β
β β β β
β βΌ β β
β Store in Cache βββββββββββ β
β β β
β βΌ β
β Response β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Located in src/app.module.ts:
CacheModule.registerAsync({
isGlobal: true,
useFactory: async (configService: ConfigService) => ({
store: await redisStore({
host: configService.get('REDIS_HOST', 'localhost'),
port: configService.get('REDIS_PORT', 6379),
}),
ttl: 60000, // 60 seconds default TTL
}),
}),| Aspect | Value |
|---|---|
| Store | Redis |
| Default TTL | 60 seconds |
| Scope | Global (available to all modules) |
| Connection | Automatic via environment variables |
- Performance: Sub-millisecond read/write operations
- Shared Cache: All server instances share the same cache (horizontal scaling ready)
- Persistence: Cache survives server restarts (if configured)
- TTL Management: Automatic expiration handling
The API implements multi-tier rate limiting using @nestjs/throttler to protect against abuse:
| Tier | Time Window | Max Requests | Purpose |
|---|---|---|---|
| Short | 1 second | 3 requests | Prevents rapid-fire requests |
| Medium | 10 seconds | 20 requests | Limits burst traffic |
| Long | 60 seconds | 100 requests | Overall usage cap |
// src/app.module.ts
ThrottlerModule.forRoot([
{
name: 'short',
ttl: 1000, // 1 second
limit: 3,
},
{
name: 'medium',
ttl: 10000, // 10 seconds
limit: 20,
},
{
name: 'long',
ttl: 60000, // 60 seconds
limit: 100,
},
]),When you exceed the rate limit, you'll receive:
{
"statusCode": 429,
"message": "ThrottlerException: Too Many Requests"
}Responses include headers showing your current rate limit status:
X-RateLimit-Limit: Maximum requests allowedX-RateLimit-Remaining: Requests remaining in current windowX-RateLimit-Reset: Time until the limit resets
nexus-backend/
βββ π src/
β βββ π auth/ # Authentication module
β β βββ dto/ # Data transfer objects
β β βββ guards/ # JWT & Roles guards
β β βββ strategies/ # Passport JWT strategy
β β βββ auth.service.ts # Auth business logic
β β
β βββ π users/ # User management module
β β βββ dto/
β β βββ users.service.ts
β β
β βββ π products/ # Product catalog module
β β βββ dto/ # Create, Update, Query DTOs
β β βββ products.service.ts
β β
β βββ π cart/ # Shopping cart module
β β βββ dto/
β β βββ cart.service.ts
β β
β βββ π orders/ # Order management module
β β βββ dto/
β β βββ orders.service.ts
β β
β βββ π checkout/ # Payment processing module
β β βββ checkout.service.ts
β β
β βββ π common/ # Shared utilities
β β βββ decorators/ # Custom decorators (@Public, @Roles)
β β βββ filters/ # Exception filters
β β βββ interceptors/ # Logging interceptor
β β
β βββ π database/ # Database layer
β β βββ schemas/ # Drizzle table schemas
β β βββ database.module.ts
β β βββ drizzle.service.ts
β β
β βββ app.module.ts # Root module
β βββ main.ts # Application entry point
β
βββ π scripts/
β βββ create-admin.ts # Admin user creation CLI
β
βββ π seed.ts # Database seeding script
βββ π docker-compose.yml # Docker services config
βββ π drizzle.config.ts # Drizzle configuration
βββ π .env.example # Environment template
βββ π package.json # Dependencies & scripts
| Method | Endpoint | Description | Access |
|---|---|---|---|
POST |
/auth/register |
Register new user | Public |
POST |
/auth/login |
Login & get tokens | Public |
POST |
/auth/logout |
Logout user | Authenticated |
| Method | Endpoint | Description | Access |
|---|---|---|---|
GET |
/products |
List products (cursor-paginated) | Public |
GET |
/products/:id |
Get product by ID | Public |
GET |
/products/categories |
Get all categories | Public |
POST |
/products |
Create product | Admin |
PATCH |
/products/:id |
Update product | Admin |
DELETE |
/products/:id |
Delete product | Admin |
| Parameter | Type | Description |
|---|---|---|
cursor |
string | Pagination cursor (product ID) |
limit |
number | Results per page (default: 10) |
category |
string | Filter by category |
search |
string | Search in product name |
minPrice |
number | Minimum price filter |
maxPrice |
number | Maximum price filter |
| Method | Endpoint | Description | Access |
|---|---|---|---|
GET |
/cart |
Get user's cart | Authenticated |
POST |
/cart/items |
Add item to cart | Authenticated |
PATCH |
/cart/items/:id |
Update item quantity | Authenticated |
DELETE |
/cart/items/:id |
Remove item from cart | Authenticated |
DELETE |
/cart |
Clear entire cart | Authenticated |
| Method | Endpoint | Description | Access |
|---|---|---|---|
POST |
/orders |
Create order from cart | Authenticated |
GET |
/orders |
Get order history | Authenticated |
GET |
/orders/:id |
Get order details | Authenticated |
| Method | Endpoint | Description | Access |
|---|---|---|---|
POST |
/checkout/pay |
Process payment (mock) | Authenticated |
# Development
pnpm start:dev # Start development server with hot reload
pnpm start:debug # Start with debugger attached
# Production
pnpm build # Build for production
pnpm start:prod # Run production build
# Database
pnpm db:push # Push schema changes to database
pnpm db:seed # Seed database with sample data
pnpm db:studio # Open Drizzle Studio (visual DB browser)
pnpm db:create-admin # Create new admin user (interactive)
# Code Quality
pnpm lint # Run ESLint
pnpm format # Format code with Prettier
# Testing
pnpm test # Run unit tests
pnpm test:watch # Run tests in watch mode
pnpm test:cov # Generate test coverage report
pnpm test:e2e # Run end-to-end testsGitHub: https://github.com/TheSamadAzeez/nexus-backend
| Item | Status | Description |
|---|---|---|
| π API Documentation (Swagger) | β | Available at /api endpoint |
| π README with Setup Instructions | β | This document |
| π§ Sample .env File | β | .env.example included |
| ποΈ Database Schema/Migrations | β | Drizzle schemas in src/database/schemas/ |
The application uses Docker Compose for local development:
| Service | Container Name | Port | Purpose |
|---|---|---|---|
| PostgreSQL | nexus-postgres |
5432 | Primary database |
| Redis | nexus-redis |
6379 | Caching layer |
| Resource | URL |
|---|---|
| API Server | http://localhost:3000 |
| Swagger Documentation | http://localhost:3000/api |
| Drizzle Studio | https://local.drizzle.studio |
After running pnpm db:seed, the following users are available:
| Password | Role | |
|---|---|---|
admin@nexus.com |
admin123 |
ADMIN |
user@nexus.com |
user123 |
USER |
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
Built with β€οΈ using NestJS, Drizzle ORM, PostgreSQL, and Redis