A production-ready RESTful API backend built with NestJS, designed to power modern portfolio applications with enterprise-grade authentication, authorization, and security features. This backend serves as a robust foundation for personal portfolio platforms, demonstrating professional backend development practices and architectural patterns suitable for scalable applications.
This API implements comprehensive user management, multi-factor authentication, role-based access control, and security best practices including rate limiting, brute-force protection, and audit logging. The architecture is designed for maintainability, testability, and horizontal scalability, making it suitable for both personal projects and production deployments.
- Runtime: Node.js 18+
- Framework: NestJS 10.x (TypeScript)
- Database: PostgreSQL 14+
- ORM: Prisma 5.x
- Cache: Redis 7.x
- Email: Nodemailer with SMTP
- Password Hashing: Argon2
- JWT: jsonwebtoken with RS256
- 2FA: Speakeasy (TOTP), Email OTP
- Encryption: AES-256-GCM
- Session Management: Database-backed with Redis
- Containerization: Podman/Docker
- Validation: class-validator, class-transformer
- Logging: Winston (planned)
- Monitoring: Health checks endpoint
┌─────────────────────────────────────────────────────────────┐
│ Client Layer │
│ (Web App, Mobile App, Third-party Services) │
└────────────────────────────┬────────────────────────────────┘
│ HTTPS
▼
┌─────────────────────────────────────────────────────────────┐
│ API Gateway Layer │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Global Middleware (Helmet, CORS, Validation) │ │
│ └────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Rate Limiting & Throttling (Redis-backed) │ │
│ └────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Authentication Guard (JWT Validation) │ │
│ └────────────────────────────────────────────────────┘ │
└────────────────────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Application Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌─────────────────┐ │
│ │ Auth Module │ │ User Module │ │ CASL Module │ │
│ │ │ │ │ │ (Authorization) │ │
│ │ - Login │ │ - Profile │ │ │ │
│ │ - Register │ │ - Settings │ │ - Policies │ │
│ │ - 2FA │ │ - Sessions │ │ - Abilities │ │
│ │ - Reset │ │ │ │ │ │
│ └──────────────┘ └──────────────┘ └─────────────────┘ │
└────────────────────────────┬────────────────────────────────┘
│
┌────────────────────┼────────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ PostgreSQL │ │ Redis │ │ Email Service│
│ Database │ │ Cache │ │ (SMTP) │
│ │ │ │ │ │
│ - Users │ │ - Sessions │ │ - 2FA OTP │
│ - Sessions │ │ - Rate Limit │ │ - Verification│
│ - Tokens │ │ - Lockout │ │ - Reset │
│ - Audit Logs │ │ │ │ │
└──────────────┘ └──────────────┘ └──────────────┘
Modular Architecture: The application follows NestJS module-based architecture, ensuring separation of concerns and independent scalability of features.
Security-First Design: All endpoints are secured by default with JWT authentication. Rate limiting and brute-force protection are implemented at the infrastructure level using Redis.
Stateless Authentication: JWT tokens enable horizontal scaling without shared session state. Refresh token rotation ensures security while maintaining user experience.
Database-Agnostic ORM: Prisma provides type-safe database access with migration support, making it easy to switch databases or add read replicas.
Redis for Performance: Redis serves as a distributed cache for rate limiting, account lockout tracking, and OTP storage, enabling multi-instance deployments.
- User registration with email/username
- Flexible login (email or username)
- User profile management
- Account settings
- Session management (view and revoke active sessions)
- Email verification with token expiration
- Password reset flow with single-use tokens
- JWT-based authentication with access and refresh tokens
- Secure token rotation on refresh
- Configurable token expiration
- Multiple active sessions support
- Device and IP tracking per session
- Automatic session cleanup
- Role-Based Access Control (RBAC) with CASL
- Three-tier role system: OWNER, ADMIN, USER
- Granular permission management
- Resource-level access control
- Ability-based authorization
- TOTP-based 2FA (Google Authenticator)
- Email-based OTP
- QR code generation for TOTP setup
- Recovery codes (10 single-use codes)
- Configurable 2FA requirement
- Argon2 password hashing (memory-hard algorithm)
- Password strength validation
- Secure password reset flow
- Password change with current password verification
- Automatic session invalidation on password change
- Account lockout after 5 failed login attempts
- Configurable lockout duration (15 minutes default)
- IP-based and user-based tracking
- Security alert emails on lockout
- Manual unlock capability
- Global rate limiting with Redis backend
- Endpoint-specific limits:
- Login: 5 attempts per minute
- Registration: 3 attempts per 5 minutes
- Password reset: 3 attempts per 15 minutes
- OTP verification: 5 attempts per 15 minutes
- OTP resend: 3 attempts per 15 minutes
- AES-256-GCM encryption for TOTP secrets
- Argon2 hashing for passwords and OTP codes
- Secure random token generation
- Token hashing before storage
- Comprehensive audit logging for security events
- Login attempt tracking with IP addresses
- 2FA event logging (enable, disable, verify)
- Session activity tracking
- User action history
- RESTful endpoint design
- Request validation with DTOs
- Type-safe responses
- Error handling with standardized formats
- Request/response transformation
- Database transactions for critical operations
- Foreign key constraints
- Unique constraints enforcement
- Cascade delete rules
- Referential integrity
- Helmet.js integration
- CORS configuration
- Cookie security flags
- Content Security Policy (CSP)
- HSTS support
- HTML email templates with Handlebars
- Responsive email design
- Email delivery for:
- Account verification
- Password reset
- 2FA setup confirmation
- Account lockout alerts
- Security notifications
- Environment-based configuration
- Centralized config service
- Validation of environment variables
- Development/production profiles
-
Registration
- User submits credentials
- Password hashed with Argon2
- Verification email sent
- Account created in pending state
-
Email Verification
- User receives verification link
- Token validated (24-hour expiry)
- Account activated
- User can now log in
-
Login
- Credentials validated
- Account lockout checked
- 2FA challenge issued (if enabled)
- JWT tokens generated
- Session created
-
2FA Verification
- TOTP code or Email OTP validated
- Rate limiting enforced
- Recovery codes accepted as fallback
- Access granted on success
-
Token Refresh
- Refresh token validated
- Old session invalidated
- New token pair generated
- Token rotation completed
Token Security
- Access tokens: 15-minute expiration
- Refresh tokens: 7-day expiration
- Stored refresh tokens in database
- Token blacklisting on logout
- Automatic cleanup of expired tokens
Encryption Standards
- AES-256-GCM for sensitive data
- Unique IV per encryption
- Authentication tags for integrity
- Secure key management via environment variables
Attack Prevention
- SQL injection prevention via Prisma ORM
- XSS protection via validation pipes
- CSRF protection ready (when using cookies)
- Brute-force protection via rate limiting
- Email enumeration prevention
POST /auth/register Register new account
POST /auth/login Authenticate user
POST /auth/refresh Refresh access token
POST /auth/logout Invalidate session
GET /auth/me Get current user
POST /auth/verify-email Verify email address
POST /auth/resend-verification Resend verification email
POST /auth/forgot-password Request password reset
GET /auth/reset-password/:token Validate reset token
POST /auth/reset-password Reset password
POST /auth/change-password Change password (authenticated)
POST /auth/2fa/enable Enable 2FA (TOTP or Email)
POST /auth/2fa/verify-setup Verify TOTP setup
POST /auth/2fa/disable Disable 2FA
GET /auth/2fa/status Get 2FA status
POST /auth/2fa/resend Resend email OTP
Register
POST /auth/register
Content-Type: application/json
{
"name": "John Doe",
"username": "johndoe",
"email": "john@example.com",
"password": "SecurePass123!",
"image": "https://example.com/avatar.jpg"
}Response
{
"user": {
"id": "cm4xr8wqz0000v8xy12abc123",
"name": "John Doe",
"username": "johndoe",
"email": "john@example.com",
"role": "USER",
"emailVerifiedAt": null,
"createdAt": "2026-01-15T16:30:00.000Z"
},
"tokens": {
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
},
"message": "Please check your email to verify your account"
}Login with 2FA
# Step 1: Initial login
POST /auth/login
{
"identifier": "john@example.com",
"password": "SecurePass123!"
}
# Response: 2FA challenge
{
"requires2FA": true,
"method": "google"
}
# Step 2: Submit 2FA code
POST /auth/login
{
"identifier": "john@example.com",
"password": "SecurePass123!",
"twoFactorToken": "123456"
}
# Response: Successful authentication
{
"user": { ... },
"tokens": { ... }
}users - Identity and profile data
- id, name, username, email, image
- role (OWNER | ADMIN | USER)
- emailVerifiedAt, createdAt, updatedAt
accounts - Authentication credentials
- id, userId, passwordHash, provider
- lastLoginAt, createdAt, updatedAt
sessions - Active user sessions
- id, userId, token, expiresAt
- createdAt
two_factor_auths - 2FA configuration
- id, userId, type (TOTP | EMAIL)
- secret (encrypted), enabled, recoveryCodes
- createdAt, updatedAt
email_otps - Email OTP tracking
- id, userId, otpHash, attempts
- expiresAt, createdAt
verification_tokens - Email verification
- id, userId, token (hashed)
- expiresAt, createdAt
password_reset_tokens - Password reset
- id, userId, token (hashed), used
- expiresAt, createdAt
login_attempts - Security tracking
- id, identifier, ipAddress, success
- createdAt
audit_logs - Security events
- id, userId, action, method
- success, ipAddress, userAgent, metadata
- createdAt
- User → Account (1:1)
- User → Sessions (1:N)
- User → TwoFactorAuths (1:N)
- User → EmailOTPs (1:N)
Create a .env file in the project root:
# Application
NODE_ENV=development
PORT=3000
APP_URL=http://localhost:3000
# Database
DATABASE_URL="postgresql://user:password@localhost:5432/portfolio"
# Redis
REDIS_URL="redis://localhost:6379"
# JWT
JWT_SECRET="your-super-secret-jwt-key-change-in-production"
JWT_REFRESH_SECRET="your-super-secret-refresh-key-change-in-production"
JWT_EXPIRES_IN="15m"
JWT_REFRESH_EXPIRES_IN="7d"
# SMTP Email
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password
SMTP_FROM="Portfolio <noreply@example.com>"
# Two-Factor Authentication
ENCRYPTION_KEY="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
OTP_EXPIRY_MINUTES=5
MAX_OTP_ATTEMPTS=5
MAX_RESEND_ATTEMPTS=3
LOCKOUT_MINUTES=15
# Email Verification
EMAIL_VERIFICATION_EXPIRY_HOURS=24
REQUIRE_EMAIL_VERIFICATION=false
# Password Reset
PASSWORD_RESET_EXPIRY_MINUTES=60
# Account Lockout
MAX_LOGIN_ATTEMPTS=5
LOCKOUT_DURATION_MINUTES=15
# Frontend URL (for email links)
FRONTEND_URL=http://localhost:3000JWT Secrets
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"Encryption Key (must be 32 bytes = 64 hex characters)
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"- Node.js 18+ and npm 9+
- PostgreSQL 14+
- Redis 7+
- Podman or Docker (for containerized deployment)
- Clone the repository
git clone <repository-url>
cd portfolio-api- Install dependencies
npm install- Set up environment variables
cp .env.example .env
# Edit .env with your configuration- Generate Prisma Client
npx prisma generate- Run database migrations
npx prisma migrate dev- Seed database (optional)
npm run seed- Start development server
npm run start:devThe API will be available at http://localhost:3000
Create a new migration
npx prisma migrate dev --name migration_nameReset database
npx prisma migrate resetOpen Prisma Studio (Database GUI)
npx prisma studioGenerate ERD
npx prisma generate
npx prisma-erd-generatorportfolio-api/
├── prisma/
│ ├── migrations/ # Database migrations
│ └── schema.prisma # Database schema
├── src/
│ ├── auth/ # Authentication module
│ │ ├── dto/ # Data Transfer Objects
│ │ ├── guards/ # Auth guards
│ │ ├── services/ # Auth services
│ │ ├── strategies/ # Passport strategies
│ │ ├── templates/ # Email templates
│ │ ├── utils/ # Crypto utilities
│ │ ├── auth.controller.ts
│ │ ├── auth.service.ts
│ │ └── auth.module.ts
│ ├── casl/ # Authorization (CASL)
│ │ ├── decorators/ # Custom decorators
│ │ ├── guards/ # Authorization guards
│ │ ├── casl-ability.factory.ts
│ │ └── casl.module.ts
│ ├── config/ # Configuration
│ │ ├── configuration.ts # App configuration
│ │ └── env.validation.ts
│ ├── prisma/ # Prisma service
│ │ ├── prisma.service.ts
│ │ └── prisma.module.ts
│ ├── redis/ # Redis service
│ │ ├── redis.service.ts
│ │ └── redis.module.ts
│ ├── app.controller.ts # Root controller
│ ├── app.service.ts # Root service
│ ├── app.module.ts # Root module
│ └── main.ts # Application entry
├── test/ # E2E tests
├── .env.example # Environment template
├── .gitignore
├── nest-cli.json
├── package.json
├── tsconfig.json
└── README.md
This project follows NestJS best practices and TypeScript conventions:
- Naming: PascalCase for classes, camelCase for variables/functions
- File naming: kebab-case (e.g.,
two-factor.service.ts) - Module structure: One feature per module
- Dependency injection: Constructor-based DI
- DTOs: class-validator decorators for validation
- Error handling: NestJS built-in exceptions
# Unit tests
npm run test
# E2E tests
npm run test:e2e
# Test coverage
npm run test:cov
# Watch mode
npm run test:watchCreate Dockerfile (if not exists)
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
COPY prisma ./prisma/
RUN npm ci
COPY . .
RUN npm run build
RUN npx prisma generate
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/prisma ./prisma
COPY package*.json ./
EXPOSE 3000
CMD ["npm", "run", "start:prod"]Build with Podman
podman build -t portfolio-api:latest .Run with Podman
podman run -d \
--name portfolio-api \
-p 3000:3000 \
--env-file .env \
portfolio-api:latestpodman-compose.yml or docker-compose.yml
version: '3.8'
services:
api:
build: .
ports:
- '3000:3000'
environment:
NODE_ENV: production
DATABASE_URL: postgresql://postgres:password@db:5432/portfolio
REDIS_URL: redis://redis:6379
env_file:
- .env
depends_on:
- db
- redis
restart: unless-stopped
db:
image: postgres:14-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: portfolio
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
restart: unless-stopped
volumes:
postgres_data:
redis_data:Deploy with Podman Compose
podman-compose up -dProduction Environment Variables
- Use strong, unique secrets for JWT keys
- Generate a new 32-byte encryption key
- Configure production SMTP settings
- Set
NODE_ENV=production - Enable email verification:
REQUIRE_EMAIL_VERIFICATION=true - Use production database with connection pooling
- Configure Redis with persistence
The API includes health check endpoints for monitoring:
GET /health # Basic health check
GET /health/db # Database connectivity
GET /health/redis # Redis connectivityProduction logging recommendations:
- Use Winston for structured logging
- Log to files and external services
- Include correlation IDs
- Separate error logs
- Monitor log aggregation
# Development
npm run start:dev # Start with hot-reload
npm run start:debug # Start with debugger
# Production
npm run build # Build for production
npm run start:prod # Start production server
# Database
npm run prisma:generate # Generate Prisma Client
npm run prisma:migrate # Run migrations
npm run prisma:studio # Open Prisma Studio
npm run prisma:seed # Seed database
# Testing
npm run test # Run unit tests
npm run test:e2e # Run E2E tests
npm run test:cov # Generate coverage
# Code Quality
npm run lint # Run ESLint
npm run format # Format with Prettiertest/
├── unit/ # Unit tests
│ ├── auth.service.spec.ts
│ ├── two-factor.service.spec.ts
│ └── ...
├── e2e/ # End-to-end tests
│ ├── auth.e2e-spec.ts
│ ├── user.e2e-spec.ts
│ └── ...
└── fixtures/ # Test data
Unit Tests
- Service layer logic testing
- Isolated component testing
- Mock external dependencies
- Test edge cases and error handling
Integration Tests
- API endpoint testing
- Database interaction testing
- Authentication flow testing
- Authorization testing
E2E Tests
- Complete user workflows
- Multi-step processes (registration → verification → login)
- 2FA flows
- Password reset flows
- Overall: 80%+
- Services: 90%+
- Controllers: 85%+
- Critical paths: 100%
{
"statusCode": 400,
"message": "Validation failed",
"error": "Bad Request",
"details": [
{
"field": "email",
"message": "Email must be a valid email address"
}
]
}200 OK- Successful request201 Created- Resource created400 Bad Request- Validation error401 Unauthorized- Authentication required403 Forbidden- Insufficient permissions404 Not Found- Resource not found409 Conflict- Resource conflict (e.g., duplicate email)429 Too Many Requests- Rate limit exceeded500 Internal Server Error- Server error
Custom exception filters handle:
- Validation errors
- Database errors
- Authentication errors
- Rate limiting errors
- Unhandled exceptions
Database
- Index optimization on frequently queried fields
- Connection pooling with Prisma
- Query optimization with select/include
- Pagination for large datasets
- Soft deletes for data retention
Caching Strategy
- Redis for rate limiting data
- Session caching
- OTP storage with TTL
- Account lockout tracking
API Performance
- Response compression
- Request validation at edge
- Database query batching
- Lazy loading of relations
Horizontal Scaling
- Stateless authentication with JWT
- Redis for shared state
- Database connection pooling
- Load balancer ready
Vertical Scaling
- Efficient resource utilization
- Memory-efficient password hashing
- Async operations where applicable
- Background job processing (planned)
Database Scaling
- Read replicas support (Prisma)
- Connection pooling
- Query optimization
- Partitioning strategy (future)
Version 1.1
- Swagger/OpenAPI documentation
- Admin dashboard endpoints
- User profile CRUD operations
- OAuth2 providers (Google, GitHub)
- Kubernetes deployment manifests
- CI/CD pipeline (GitHub Actions)
- Automated database backups
- Performance monitoring (APM)
- Security scanning in CI
This project is licensed under the MIT License - see the LICENSE file for details.
Rizal Achmad Saputra
This backend API was developed as a demonstration of production-ready backend development practices, showcasing enterprise-grade authentication, security patterns, and architectural decisions suitable for scalable applications.
For questions, suggestions, or collaboration opportunities, please open an issue in the repository.
Note: This is a personal portfolio backend designed to demonstrate professional backend development skills. All security configurations, environment variables, and deployment settings should be reviewed and adjusted according to specific production requirements.