A modern backend system built with Node.js using a Clean Architecture approach, integrating multiple databases, authentication flows, caching, and an event-driven system. The project also includes a complete DevOps setup with Docker, Kubernetes, and a Jenkins CI/CD pipeline.
This project demonstrates a real-world backend architecture that combines:
- REST API with Express.js
- PostgreSQL for core relational data
- MongoDB for activity logging
- Redis for caching and temporary storage
- Apache Kafka for event-driven communication
- JWT Authentication with 2FA and password reset flow
- Google OAuth 2.0 authentication
- Docker & Kubernetes for containerization and orchestration
- Jenkins CI/CD pipeline for automated builds and deployments
The project follows a layered Clean Architecture structure:
src/
โโโ config/ # Environment configuration
โโโ controllers/ # Request handlers
โโโ services/ # Business logic
โโโ repositories/ # Database queries
โโโ routes/ # API route definitions
โโโ middleware/ # Auth, validation, error handling
โโโ db/ # Database connections
โโโ kafka/ # Kafka producer/consumer
โโโ models/ # MongoDB models
โโโ utils/ # Helpers (JWT, mail, async)
Route โ Controller โ Service โ Repository โ Database
| Category | Technology |
|---|---|
| Runtime | Node.js (ES Modules) |
| Framework | Express.js |
| Primary Database | PostgreSQL (pg) |
| Activity Logs | MongoDB (mongoose) |
| Caching & Tokens | Redis |
| Event Streaming | Apache Kafka (kafkajs) |
| Authentication | JWT + 2FA + Nodemailer |
| OAuth | Google OAuth 2.0 (passport-google-oauth20) |
| Containerization | Docker & Docker Compose |
| Orchestration | Kubernetes (kubectl) |
| CI/CD | Jenkins Pipeline |
| Testing | Jest + Code Coverage |
- User Registration
- Login with Email + Password
- 2FA verification (email-based OTP)
- JWT Access Token (short-lived, 15 minutes)
- Refresh Token (httpOnly cookie, 7 days)
- Forgot Password (email reset link)
- Reset Password (token-based)
- Logout (invalidates refresh token in Redis)
- Get current user (
/me) - Update profile (
/profile)
- Login with Google account (no password required)
- Automatic user creation on first login
- Issues JWT access & refresh tokens same as email login
- Fully integrated with existing user system
- Protected routes using JWT middleware
- Role-based access control (admin / user)
- Only authenticated users can create, update, and delete posts
- Ownership-based authorization โ users can only modify their own posts
- Admin-only access to full activity logs
Kafka is used for decoupled event processing. All major actions publish events to a Kafka topic which are consumed and stored as activity logs in MongoDB.
USER_REGISTEREDLOGIN_2FA_SENTUSER_LOGGED_INLOGIN_FAILEDPASSWORD_RESET_REQUESTEDPASSWORD_RESET_COMPLETEDUSER_LOGGED_OUTPROFILE_UPDATEDPOST_CREATEDPOST_UPDATEDPOST_DELETED
Post/Auth Action โ Kafka Producer โ Kafka Topic โ Consumer โ MongoDB Logs
- 2FA codes โ
2fa:email(expires in 5 minutes) - Password reset tokens โ
reset:token(expires in 10 minutes) - Refresh tokens โ
refresh:userId - Optional caching layer for performance
Stores: users (with google_id column for OAuth users), posts
Stores: activity logs
Stores: temporary/authentication data
The backend is containerized using Docker. A custom Dockerfile uses node:20-alpine for a lightweight production image. Jenkins runs as a separate Docker container via Docker Compose.
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 5000
CMD ["node", "index.js"]All services are deployed in a Kubernetes cluster using Docker Desktop. Each service runs as a Deployment with a corresponding Service for internal cluster communication.
| Service | Image | Port |
|---|---|---|
| Backend (Node.js) | research-app:latest | NodePort 30007 |
| PostgreSQL | postgres:16 | 5432 (ClusterIP) |
| MongoDB | mongo:7 | 27017 (ClusterIP) |
| Redis | redis:7-alpine | 6379 (ClusterIP) |
| Kafka (KRaft) | apache/kafka:3.7.0 | 9092 (ClusterIP) |
- ConfigMap โ non-sensitive environment variables (DB_HOST, REDIS_URL, GOOGLE_CALLBACK_URL, etc.)
- Secrets โ sensitive variables (DB_PASSWORD, JWT secrets, mail credentials, Google OAuth credentials)
- PersistentVolumeClaims โ data persistence for PostgreSQL and MongoDB
- InitContainers โ backend waits for PostgreSQL, Redis, MongoDB and Kafka before starting
- ReadinessProbe & LivenessProbe โ health checks on all services
k8s/base/
โโโ configmap.yaml
โโโ secrets.yaml
โโโ postgres-deployment.yaml
โโโ redis-deployment.yaml
โโโ mongo-deployment.yaml
โโโ kafka-deployment.yaml
โโโ backend-deployment.yaml
Jenkins runs as a Docker container and is integrated with Kubernetes for automated deployments. The pipeline is triggered automatically via GitHub webhook on every push to the main branch using ngrok for tunnel exposure.
- Clean Workspace โ removes old files
- Checkout Code โ pulls latest code from GitHub
- Install Dependencies โ
npm ci - Run Tests โ
node --test - Build Docker Image โ tagged with build number (
research-app:BUILD_NUMBER) - Deploy to Kubernetes โ
kubectl set imagewith new tag - Verify Deploy โ
kubectl get pods
git push โ GitHub โ Webhook โ ngrok โ Jenkins โ Build โ Deploy โ Kubernetes
Create a .env file:
PORT=5000
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=your_password
DB_NAME=app_db
MONGO_URI=mongodb://localhost:27017/activityLog
KAFKA_BROKER=localhost:9092
KAFKA_CLIENT_ID=backend-app
KAFKA_TOPIC_POSTS=post-events
KAFKA_GROUP_ID=post-events-group
KAFKAJS_NO_PARTITIONER_WARNING=1
REDIS_URL=redis://127.0.0.1:6379
FRONTEND_ORIGIN=http://localhost:3000
JWT_ACCESS_SECRET=your_access_secret
JWT_ACCESS_EXPIRES_IN=15m
JWT_REFRESH_SECRET=your_refresh_secret
JWT_REFRESH_EXPIRES_IN=7d
TWO_FA_EXPIRES_SECONDS=300
RESET_PASSWORD_EXPIRES_SECONDS=600
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USER=your_email@gmail.com
MAIL_PASS=your_app_password
MAIL_FROM=your_email@gmail.com
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
GOOGLE_CALLBACK_URL=http://localhost:30007/api/auth/google/callbackkubectl apply -f k8s/base/secrets.yaml
kubectl apply -f k8s/base/configmap.yaml
kubectl apply -f k8s/base/postgres-deployment.yaml
kubectl apply -f k8s/base/redis-deployment.yaml
kubectl apply -f k8s/base/mongo-deployment.yaml
kubectl apply -f k8s/base/kafka-deployment.yaml
kubectl apply -f k8s/base/backend-deployment.yamldocker-compose up -dAfter starting Jenkins, copy the kubeconfig so Jenkins can communicate with the Kubernetes cluster:
docker exec -u root jenkins mkdir -p /var/jenkins_home/.kube
docker cp "$env:USERPROFILE\.kube\config" jenkins:/var/jenkins_home/.kube/config
docker exec -u root jenkins chown -R jenkins:jenkins /var/jenkins_home/.kube
โ ๏ธ This step is required every time the Jenkins container is recreated.
kubectl get podscurl http://localhost:30007/
# Expected: {"success":true,"message":"API is running"}Open in browser:
http://localhost:30007/api/auth/google
| Method | Endpoint | Access |
|---|---|---|
| POST | /api/auth/register |
Public |
| POST | /api/auth/login |
Public |
| POST | /api/auth/verify-2fa |
Public |
| POST | /api/auth/refresh-token |
Public |
| POST | /api/auth/forgot-password |
Public |
| POST | /api/auth/reset-password |
Public |
| POST | /api/auth/logout |
Auth |
| GET | /api/auth/me |
Auth |
| PUT | /api/auth/profile |
Auth |
| GET | /api/auth/google |
Public |
| GET | /api/auth/google/callback |
Public |
| Method | Endpoint | Access |
|---|---|---|
| POST | /api/posts |
Auth |
| GET | /api/posts |
Public |
| GET | /api/posts/my-posts |
Auth |
| PUT | /api/posts/:id |
Owner / Admin |
| DELETE | /api/posts/:id |
Owner / Admin |
| Method | Endpoint | Access |
|---|---|---|
| GET | /api/activity-logs |
Admin only |
| GET | /api/activity-logs/my-logs |
Auth |
The project uses Jest as the testing framework with code coverage reporting.
npm test| File | Coverage |
|---|---|
| auth.service.js | ~21% |
| post.service.js | ~37% |
| jwt.js | ~50% |
| Overall | ~13% |
| File | Tests |
|---|---|
| simple.test.js | Basic unit tests |
| auth.service.test.js | Auth service tests with mocking |
| post.service.test.js | Post service tests with mocking |
- ESLint โ code quality and linting rules
Getuar Jakupi Computer Science & Engineering Student | Full-Stack Developer ๐ Email: getuar.j1@gmail.com