A scalable, production-ready NestJS microservices backend for an e-commerce platform, orchestrated with Turborepo for efficient monorepo management. Services communicate independently and are fully containerized with Docker. The platform supports two parallel deployment paths:
- Google Cloud — Cloud Build → Artifact Registry → GKE
- AWS — CodePipeline/CodeBuild → Elastic Container Registry (ECR) → EKS
http://k8s-default-ecommerc-a819a1b0cb-993668267.ap-south-1.elb.amazonaws.com
ecommerce-baseline/
├── apps/ # (Reserved for future front-end apps)
├── libs/ # Shared libraries & utilities
├── k8s/
│ └── ecommerce/ # Kubernetes manifests for all services
├── packages/ # Shared packages (configs, types, etc.)
├── services/
│ ├── auth-service/ # Authentication & authorization microservice
│ ├── notification-service/ # Notification delivery microservice
│ ├── orders-service/ # Order management microservice
│ ├── payments-service/ # Payment processing microservice
│ └── products/ # Product catalog microservice
├── .dockerignore
├── .env # Environment variables (local)
├── .env.example # Environment variable template
├── .gitignore
├── buildspec.yaml # AWS CodeBuild/CodePipeline build spec
├── cloudbuild.yaml # Google Cloud Build CI/CD pipeline
├── cluster.yaml # eksctl config for AWS EKS cluster creation
├── docker-compose.yaml # Root-level multi-service orchestration
├── package.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml # PNPM workspace config
├── tsconfig.json # Base TypeScript config
└── turbo.json # Turborepo pipeline config
Handles all authentication and authorization concerns for the platform.
Responsibilities:
- User registration and login
- JWT token generation and validation
- Password hashing and verification
- Session/token management
Manages outbound email notifications via SMTP using Google OAuth2 for secure, token-based authentication.
Responsibilities:
- Email delivery via SMTP with Google OAuth2 authentication
- OAuth2 access token retrieval using a refresh token
- Event-driven notification triggers
- Notification templates and rendering
- Delivery status tracking
Manages the full order lifecycle for the e-commerce platform.
Responsibilities:
- Order creation and management
- Order status tracking and updates
- Cart and checkout processing
- Order history and reporting
Handles payment processing and financial transactions via the Stripe payment gateway.
Responsibilities:
- Payment charge creation and processing via Stripe
- Stripe webhook handling and event verification
- Transaction history and reconciliation
- Refund and dispute management
Manages the product catalog for the e-commerce platform.
Responsibilities:
- CRUD operations for products
- Product listing and filtering
- Inventory/stock management
- Category management
| Layer | Technology |
|---|---|
| Runtime | Node.js |
| Framework | NestJS |
| Language | TypeScript |
| Database | MongoDB |
| Monorepo | Turborepo |
| Package Manager | PNPM |
| Containerization | Docker & Docker Compose |
| Orchestration | Kubernetes (GKE / EKS) |
| CI/CD (GCP) | Google Cloud Build |
| CI/CD (AWS) | AWS CodePipeline & AWS CodeBuild |
| Image Registry | Google Artifact Registry / AWS ECR |
| Payment Gateway | Stripe |
| Email Delivery | SMTP with Google OAuth2 |
Make sure you have the following installed:
- Node.js
>= 18.x - PNPM
>= 8.x - Docker & Docker Compose
- MongoDB (or use the Docker Compose setup)
- kubectl (for Kubernetes deployments)
- Google Cloud SDK (
gcloud) (for GKE & Cloud Build) - AWS CLI (for ECR, CodePipeline & EKS)
- eksctl (for creating the AWS EKS cluster)
git clone https://github.com/Subhrotechinfo/ecommerce-backend.git
cd ecommerce-backendpnpm installcp .env.example .envEdit .env with your configuration values (MongoDB URI, JWT secrets, ports, etc.).
Spin up all services and MongoDB with a single command:
docker-compose up --buildThis starts:
auth-servicenotification-serviceorders-servicepayments-serviceproductsservice- MongoDB instance
Start all services in parallel using Turborepo:
pnpm devOr run a specific service:
pnpm --filter auth-service dev
pnpm --filter notification-service dev
pnpm --filter orders-service dev
pnpm --filter payments-service dev
pnpm --filter products devBuild all services:
pnpm buildBuild a specific service:
pnpm --filter auth-service build
pnpm --filter orders-service buildRun tests across all services:
pnpm testRun tests for a specific service:
pnpm --filter auth-service test
pnpm --filter payments-service testEach service contains its own Dockerfile. The root docker-compose.yaml orchestrates all services together.
docker build -t auth-service ./services/auth-service
docker build -t notification-service ./services/notification-service
docker build -t orders-service ./services/orders-service
docker build -t payments-service ./services/payments-service
docker build -t products-service ./services/productsdocker-compose up --builddocker-compose downdocker-compose down -vThis project uses Google Cloud Build to automate building and pushing Docker images on every commit to the main branch. The pipeline is defined in cloudbuild.yaml at the root of the repository.
- A push to
maintriggers a Cloud Build trigger (configured in GCP Console). - Cloud Build builds a Docker image for each microservice using its respective
Dockerfile. - Each image is tagged and pushed to Google Artifact Registry in the
asia-south1region under theecommerce-499513project.
steps:
# Auth Service
- name: "gcr.io/cloud-builders/docker"
args:
[
"build",
"-t",
"asia-south1-docker.pkg.dev/ecommerce-499513/auth-service/production",
"-f",
"services/auth-service/Dockerfile",
".",
]
- name: "gcr.io/cloud-builders/docker"
args:
[
"push",
"asia-south1-docker.pkg.dev/ecommerce-499513/auth-service/production",
]
# Notification Service
- name: "gcr.io/cloud-builders/docker"
args:
[
"build",
"-t",
"asia-south1-docker.pkg.dev/ecommerce-499513/notification-service/production",
"-f",
"services/notification-service/Dockerfile",
".",
]
- name: "gcr.io/cloud-builders/docker"
args:
[
"push",
"asia-south1-docker.pkg.dev/ecommerce-499513/notification-service/production",
]
# Orders Service
- name: "gcr.io/cloud-builders/docker"
args:
[
"build",
"-t",
"asia-south1-docker.pkg.dev/ecommerce-499513/orders-service/production",
"-f",
"services/orders-service/Dockerfile",
".",
]
- name: "gcr.io/cloud-builders/docker"
args:
[
"push",
"asia-south1-docker.pkg.dev/ecommerce-499513/orders-service/production",
]
# Payments Service
- name: "gcr.io/cloud-builders/docker"
args:
[
"build",
"-t",
"asia-south1-docker.pkg.dev/ecommerce-499513/payments-service/production",
"-f",
"services/payments-service/Dockerfile",
".",
]
- name: "gcr.io/cloud-builders/docker"
args:
[
"push",
"asia-south1-docker.pkg.dev/ecommerce-499513/payments-service/production",
]
# Products Service
- name: "gcr.io/cloud-builders/docker"
args:
[
"build",
"-t",
"asia-south1-docker.pkg.dev/ecommerce-499513/products/production",
"-f",
"services/products/Dockerfile",
".",
]
- name: "gcr.io/cloud-builders/docker"
args:
[
"push",
"asia-south1-docker.pkg.dev/ecommerce-499513/products/production",
]
options:
logging: CLOUD_LOGGING_ONLY- Navigate to Cloud Build → Triggers in the GCP Console.
- Click Create Trigger and connect your GitHub repository.
- Set the trigger to fire on pushes to the
mainbranch. - Point it to
cloudbuild.yamlas the build configuration file. - Ensure the Cloud Build service account has the Artifact Registry Writer IAM role.
gcloud auth configure-docker asia-south1-docker.pkg.devAll Docker images are stored in Google Artifact Registry in the asia-south1 region.
| Service | Registry Path |
|---|---|
| Auth Service | asia-south1-docker.pkg.dev/ecommerce-499513/auth-service/productions |
| Notification Service | asia-south1-docker.pkg.dev/ecommerce-499513/notification-service/productions |
| Orders Service | asia-south1-docker.pkg.dev/ecommerce-499513/orders-service/productions |
| Payments Service | asia-south1-docker.pkg.dev/ecommerce-499513/payments-service/productions |
| Products Service | asia-south1-docker.pkg.dev/ecommerce-499513/products/productions |
# Authenticate first
gcloud auth configure-docker asia-south1-docker.pkg.dev
# Pull a specific image
docker pull asia-south1-docker.pkg.dev/ecommerce-499513/auth-service/production:latestAll Kubernetes manifests live in the k8s/ecommerce/ directory. Each microservice has its own Deployment and Service manifest.
# Install kubectl
gcloud components install kubectl
# Authenticate and get GKE cluster credentials
gcloud container clusters get-credentials <CLUSTER_NAME> \
--region asia-south1 \
--project ecommerce-499513k8s/
└── ecommerce/
├── auth-service/
│ ├── deployment.yaml
│ └── service.yaml
├── notification-service/
│ ├── deployment.yaml
│ └── service.yaml
├── orders-service/
│ ├── deployment.yaml
│ └── service.yaml
├── payments-service/
│ ├── deployment.yaml
│ └── service.yaml
└── products/
├── deployment.yaml
└── service.yaml
Apply all manifests in the k8s/ecommerce/ directory:
kubectl apply -f k8s/ecommerce/kubectl apply -f k8s/ecommerce/auth-service/
kubectl apply -f k8s/ecommerce/orders-service/# List all pods
kubectl get pods
# List all services
kubectl get services
# Describe a specific deployment
kubectl describe deployment auth-service
# View logs for a specific pod
kubectl logs -f deployment/auth-serviceWhen Cloud Build pushes a new image to Artifact Registry, trigger a rolling update:
kubectl rollout restart deployment/auth-service
kubectl rollout restart deployment/orders-service
kubectl rollout restart deployment/payments-service
kubectl rollout restart deployment/notification-service
kubectl rollout restart deployment/products-service# Scale auth-service to 3 replicas
kubectl scale deployment auth-service --replicas=3kubectl delete -f k8s/ecommerce/Below is an example deployment.yaml for the Auth Service. Other services follow the same pattern:
apiVersion: apps/v1
kind: Deployment
metadata:
name: auth-service
labels:
app: auth-service
spec:
replicas: 2
selector:
matchLabels:
app: auth-service
template:
metadata:
labels:
app: auth-service
spec:
containers:
- name: auth-service
image: asia-south1-docker.pkg.dev/ecommerce-499513/auth-service/production:latest
ports:
- containerPort: 3001
envFrom:
- secretRef:
name: ecommerce-secrets
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
name: auth-service
spec:
selector:
app: auth-service
ports:
- protocol: TCP
port: 3001
targetPort: 3001
type: ClusterIPStore sensitive environment variables (JWT secrets, Stripe keys, etc.) as Kubernetes Secrets:
kubectl create secret generic ecommerce-secrets \
--from-env-file=.envReference the secret in your deployment manifests using envFrom.secretRef.
GitHub Push (main branch)
│
▼
Google Cloud Build (cloudbuild.yaml)
│
├── Build Docker images for all 5 services
│
└── Push images to Google Artifact Registry
(asia-south1-docker.pkg.dev/ecommerce-499513/...)
│
▼
kubectl rollout restart
│
▼
Google Kubernetes Engine (GKE)
Pods pull latest images
and serve traffic
As an alternative to Google Cloud Build, this project also ships an AWS CodePipeline pipeline backed by AWS CodeBuild. The build steps are defined in buildspec.yaml at the root of the repository, and images are pushed to a private Amazon ECR registry in the ap-south-1 region.
- A push to the tracked branch triggers the CodePipeline source stage (CodeCommit/GitHub/CodeStar connection).
- The CodeBuild stage authenticates to ECR, then builds a Docker image for each microservice using its respective
Dockerfile. - Each image is tagged
latestand pushed to its own private ECR repository under AWS account971598352479inap-south-1.
version: 0.2
phases:
pre_build:
commands:
- aws ecr get-login-password --region ap-south-1 | docker login --username AWS --password-stdin 971598352479.dkr.ecr.ap-south-1.amazonaws.com
build:
commands:
- docker build -t auth-service -f ./services/auth-service/Dockerfile .
- docker tag auth-service:latest 971598352479.dkr.ecr.ap-south-1.amazonaws.com/auth-service:latest
- docker build -t notification-service -f ./services/notification-service/Dockerfile .
- docker tag notification-service:latest 971598352479.dkr.ecr.ap-south-1.amazonaws.com/notification-service:latest
- docker build -t orders-service -f ./services/orders-service/Dockerfile .
- docker tag orders-service:latest 971598352479.dkr.ecr.ap-south-1.amazonaws.com/orders-service:latest
- docker build -t payments-service -f ./services/payments-service/Dockerfile .
- docker tag payments-service:latest 971598352479.dkr.ecr.ap-south-1.amazonaws.com/payments-service:latest
- docker build -t products -f ./services/products/Dockerfile .
- docker tag products:latest 971598352479.dkr.ecr.ap-south-1.amazonaws.com/products:latest
post_build:
commands:
- docker push 971598352479.dkr.ecr.ap-south-1.amazonaws.com/auth-service:latest
- docker push 971598352479.dkr.ecr.ap-south-1.amazonaws.com/notification-service:latest
- docker push 971598352479.dkr.ecr.ap-south-1.amazonaws.com/orders-service:latest
- docker push 971598352479.dkr.ecr.ap-south-1.amazonaws.com/payments-service:latest
- docker push 971598352479.dkr.ecr.ap-south-1.amazonaws.com/products:latest- Navigate to CodePipeline in the AWS Console and create a new pipeline.
- Connect the source stage to your GitHub repository (via a CodeStar Connection) or CodeCommit.
- Add a CodeBuild build stage pointing to
buildspec.yamlat the repo root. - Ensure the CodeBuild service role has permissions for
ecr:GetAuthorizationToken,ecr:BatchCheckLayerAvailability,ecr:PutImage,ecr:InitiateLayerUpload,ecr:UploadLayerPart, andecr:CompleteLayerUpload. - (Optional) Add a deploy stage that runs
kubectl rollout restartagainst the EKS cluster after a successful build.
All Docker images are stored in private Amazon ECR repositories in the ap-south-1 region under AWS account 971598352479.
| Service | Registry Path |
|---|---|
| Auth Service | 971598352479.dkr.ecr.ap-south-1.amazonaws.com/auth-service |
| Notification Service | 971598352479.dkr.ecr.ap-south-1.amazonaws.com/notification-service |
| Orders Service | 971598352479.dkr.ecr.ap-south-1.amazonaws.com/orders-service |
| Payments Service | 971598352479.dkr.ecr.ap-south-1.amazonaws.com/payments-service |
| Products Service | 971598352479.dkr.ecr.ap-south-1.amazonaws.com/products |
🔒 These repositories are private. Only IAM principals with the relevant ECR permissions can pull or push images.
aws ecr get-login-password --region ap-south-1 | \
docker login --username AWS --password-stdin 971598352479.dkr.ecr.ap-south-1.amazonaws.comdocker pull 971598352479.dkr.ecr.ap-south-1.amazonaws.com/auth-service:latestThe AWS Kubernetes cluster is provisioned using eksctl from the cluster.yaml config at the root of the repository.
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: ecommerce
region: ap-south-1
nodeGroups:
- name: ng-1
instanceType: t3.micro
minSize: 3
maxSize: 8
desiredCapacity: 6
⚠️ t2.microis not free-tier eligible in all accounts/regions — AWS returnedInstanceType not eligible for Free Tierwhen creating the nodegroup. Runaws ec2 describe-instance-types --filters "Name=free-tier-eligible,Values=true" --region ap-south-1 --query "InstanceTypes[].InstanceType" --output tableto confirm which type your account gets; for most accounts it'st3.micro.
t3.microalso only fits ~1 extra app pod per node after system pods (aws-node,kube-proxy, etc.) due to ENI/IP-based pod limits on EKS — hencedesiredCapacity: 6to give all 5 microservices a schedulable slot with a little headroom.
eksctl create cluster -f cluster.yamlThis provisions an EKS cluster named ecommerce in ap-south-1 with a managed node group (ng-1) of t3.micro nodes (min 3, max 8, desired 6).
eksctl scale nodegroup ng-1 -N 6 --cluster ecommerce -M 6aws eks update-kubeconfig --name ecommerce --region ap-south-1Once kubectl is pointed at the EKS cluster, deployment uses the same manifests as GKE:
kubectl apply -f k8s/ecommerce/ℹ️ Update the
image:field in eachdeployment.yamlto reference the corresponding ECR path (e.g.971598352479.dkr.ecr.ap-south-1.amazonaws.com/auth-service:latest) when deploying to EKS.
eksctl delete cluster -f cluster.yamlUnlike GKE, EKS does not provision a load balancer automatically for Ingress resources — the AWS Load Balancer Controller must be installed on the cluster before any Ingress manifest will result in a working ALB.
The Ingress resource uses alb.ingress.kubernetes.io annotations to tell the controller to provision an internet-facing Application Load Balancer, routing to each service by path:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ecommerce
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing # attach the load balancer to a public subnet
kubernetes.io/ingress.class: alb # provision an Application Load Balancer
spec:
rules:
- http:
paths:
- path: /products/*
pathType: ImplementationSpecific
backend:
service:
name: products
port:
number: 5000
- path: /auth/*
pathType: ImplementationSpecific
backend:
service:
name: auth-service-http
port:
number: 5001
- path: /orders/*
pathType: ImplementationSpecific
backend:
service:
name: orders-service-http
port:
number: 5005http://k8s-default-ecommerc-a819a1b0cb-993668267.ap-south-1.elb.amazonaws.com
| Route | Service |
|---|---|
/products/* |
products (port 5000) |
/auth/* |
auth-service-http (port 5001) |
/orders/* |
orders-service-http (port 5005) |
ℹ️ A
/payments/*and/notifications/*route can be added the same way once those services expose an HTTPService— mirror the pattern above with the correct service name and port.
GitHub Push
│
▼
AWS CodePipeline (source stage)
│
▼
AWS CodeBuild (buildspec.yaml)
│
├── docker login to ECR (971598352479.dkr.ecr.ap-south-1.amazonaws.com)
├── Build Docker images for all 5 services
│
└── Push images to private Amazon ECR
│
▼
kubectl rollout restart
│
▼
Amazon EKS (cluster: ecommerce)
Pods pull latest images
│
▼
AWS Load Balancer Controller (kube-system)
reads Ingress → provisions/updates ALB
│
▼
k8s-default-ecommerc-...elb.amazonaws.com
routes /products, /auth, /orders
and serves traffic
Copy .env.example to .env and fill in the values. Key variables include:
| Variable | Description |
|---|---|
NODE_ENV |
Runtime environment (development, production) |
MONGODB_URI |
MongoDB connection string |
| Variable | Description |
|---|---|
AUTH_SERVICE_APP_NAME |
Display name for the auth service |
AUTH_SERVICE_APP_PORT |
HTTP port for the auth service |
AUTH_SERVICE_TCP_PORT |
TCP port for inter-service communication |
AUTH_HOST |
Hostname used by other services to reach auth |
JWT_SECRET |
Secret key for JWT signing |
JWT_EXPIRES_IN |
JWT token expiry duration (e.g. 3600s, 7d) |
| Variable | Description |
|---|---|
PRODUCT_SERVICE_APP_NAME |
Display name for the products service |
PRODUCT_SERVICE_APP_PORT |
HTTP port for the products service |
| Variable | Description |
|---|---|
ORDERS_SERVICE_APP_NAME |
Display name for the orders service |
ORDERS_SERVICE_APP_PORT |
HTTP port for the orders service |
ORDERS_TCP_PORT |
TCP port for inter-service communication |
| Variable | Description |
|---|---|
PAYMENTS_SERVICE_APP_NAME |
Display name for the payments service |
PAYMENTS_SERVICE_APP_PORT |
HTTP port for the payments service |
PAYMENTS_HOST |
Hostname used by other services to reach payments |
PAYMENTS_TCP_PORT |
TCP port for inter-service communication |
STRIPE_SECRET_KEY |
Stripe secret API key |
| Variable | Description |
|---|---|
NOTIFICATION_SERVICE_APP_NAME |
Display name for the notification service |
NOTIFICATION_SERVICE_APP_PORT |
HTTP port for the notification service |
NOTIFICATION_HOST |
Hostname used by other services to reach notifications |
NOTIFICATION_TCP_PORT |
TCP port for inter-service communication |
SMTP_USER |
Gmail address used as the SMTP sender |
GOOGLE_OAUTH_CLIENT_ID |
Google OAuth2 client ID for SMTP authentication |
GOOGLE_OAUTH_CLIENT_SECRET |
Google OAuth2 client secret for SMTP authentication |
GOOGLE_OAUTH_REFRESH_TOKEN |
OAuth2 refresh token to obtain SMTP access tokens |
| Variable | Description |
|---|---|
AWS_ACCOUNT_ID |
AWS account ID hosting the ECR repositories (971598352479) |
AWS_REGION |
AWS region for ECR/EKS (ap-south-1) |
EKS_CLUSTER_NAME |
Name of the EKS cluster (ecommerce) |
⚠️ Never commit your.envfile. It is listed in.gitignore.
| Method | Endpoint | Description |
|---|---|---|
POST |
/auth/register |
Register a new user |
POST |
/auth/login |
Login and receive JWT |
GET |
/auth/profile |
Get current user profile |
| Method | Endpoint | Description |
|---|---|---|
POST |
/notifications/send |
Trigger a notification |
GET |
/notifications |
List notifications |
GET |
/notifications/:id |
Get a single notification |
| Method | Endpoint | Description |
|---|---|---|
GET |
/orders |
List all orders |
GET |
/orders/:id |
Get a single order |
POST |
/orders |
Create a new order |
PATCH |
/orders/:id |
Update an order |
DELETE |
/orders/:id |
Cancel/delete an order |
| Method | Endpoint | Description |
|---|---|---|
POST |
/payments/charge |
Create a new Stripe charge |
POST |
/payments/webhook |
Receive and verify Stripe webhooks |
GET |
/payments |
List payment transactions |
GET |
/payments/:id |
Get a single transaction |
| Method | Endpoint | Description |
|---|---|---|
GET |
/products |
List all products |
GET |
/products/:id |
Get a single product |
POST |
/products |
Create a new product |
PATCH |
/products/:id |
Update a product |
DELETE |
/products/:id |
Delete a product |
Defined in turbo.json, the pipelines enable smart caching and parallel task execution across all services.
| Task | Command | Description |
|---|---|---|
build |
pnpm build |
Compiles all services; outputs to dist/. Depends on upstream builds (^build) |
dev |
pnpm dev |
Runs all services in watch mode. No cache, persistent process |
start:dev |
pnpm start:dev |
Starts services in dev mode; watches .env* files for changes |
test |
pnpm test |
Runs tests after build; outputs coverage to coverage/ |
lint |
pnpm lint |
Lints all packages; respects upstream lint order (^lint) |
check-types |
pnpm check-types |
TypeScript type-checking across all services |
Turborepo treats the following as global — any change to them invalidates the cache for all tasks:
.env— shared environment configtsconfig.json— root TypeScript configuration
- Create a new NestJS app under
services/:
nest new services/my-new-service- Add it to
pnpm-workspace.yamlif not auto-detected. - Add a
Dockerfileinside the new service. - Add the service to
docker-compose.yaml. - Add build and push steps for the new service to
cloudbuild.yaml(GCP) andbuildspec.yaml(AWS). - Create
k8s/ecommerce/my-new-service/deployment.yamlandservice.yaml. - Add any shared packages to
libs/orpackages/as needed.
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Commit your changes:
git commit -m 'feat: add my feature' - Push the branch:
git push origin feature/my-feature - Open a Pull Request
This project is licensed under the MIT License.