{"id":49562650,"url":"https://github.com/himanshu2604/acquisitions-api","last_synced_at":"2026-05-03T10:44:37.318Z","repository":{"id":348719590,"uuid":"1196190094","full_name":"himanshu2604/acquisitions-api","owner":"himanshu2604","description":"Production-grade Node.js REST API with DevSecOps CI/CD pipeline — Trivy + SonarCloud security gates, ArgoCD GitOps deployment, and Prometheus + Grafana monitoring","archived":false,"fork":false,"pushed_at":"2026-04-23T03:54:34.000Z","size":2182,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-03T10:44:26.051Z","etag":null,"topics":["arcjet","argocd","devsecops","docker","expressjs","github-actions","gitops","grafana","helm","jwt","kubernetes","neondb","nodejs","portfolio","prometheus","sonarcloud","trivy","typescript"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/himanshu2604.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-30T13:08:45.000Z","updated_at":"2026-04-23T03:53:35.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/himanshu2604/acquisitions-api","commit_stats":null,"previous_names":["himanshu2604/acquisitions-api"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/himanshu2604/acquisitions-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/himanshu2604%2Facquisitions-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/himanshu2604%2Facquisitions-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/himanshu2604%2Facquisitions-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/himanshu2604%2Facquisitions-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/himanshu2604","download_url":"https://codeload.github.com/himanshu2604/acquisitions-api/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/himanshu2604%2Facquisitions-api/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32566444,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-03T06:36:36.687Z","status":"ssl_error","status_checked_at":"2026-05-03T06:36:09.306Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arcjet","argocd","devsecops","docker","expressjs","github-actions","gitops","grafana","helm","jwt","kubernetes","neondb","nodejs","portfolio","prometheus","sonarcloud","trivy","typescript"],"created_at":"2026-05-03T10:44:36.714Z","updated_at":"2026-05-03T10:44:37.312Z","avatar_url":"https://github.com/himanshu2604.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# 🏢 Acquisitions API\n\n### Production-grade REST API for buying and selling SaaS businesses\n\nBuilt with a full **DevSecOps CI/CD pipeline**, **GitOps deployment**, and **real-time observability**\n\n\u003cbr/\u003e\n\n![Node.js](https://img.shields.io/badge/Node.js-20-339933?style=for-the-badge\u0026logo=nodedotjs\u0026logoColor=white)\n![TypeScript](https://img.shields.io/badge/TypeScript-5-3178C6?style=for-the-badge\u0026logo=typescript\u0026logoColor=white)\n![Docker](https://img.shields.io/badge/Docker-2496ED?style=for-the-badge\u0026logo=docker\u0026logoColor=white)\n![Kubernetes](https://img.shields.io/badge/Kubernetes-326CE5?style=for-the-badge\u0026logo=kubernetes\u0026logoColor=white)\n![GitHub Actions](https://img.shields.io/badge/GitHub_Actions-2088FF?style=for-the-badge\u0026logo=githubactions\u0026logoColor=white)\n![ArgoCD](https://img.shields.io/badge/ArgoCD-EF7B4D?style=for-the-badge\u0026logo=argo\u0026logoColor=white)\n\n![Trivy](https://img.shields.io/badge/Trivy-1904DA?style=for-the-badge\u0026logo=aquasecurity\u0026logoColor=white)\n![SonarCloud](https://img.shields.io/badge/SonarCloud-F3702A?style=for-the-badge\u0026logo=sonarcloud\u0026logoColor=white)\n![Prometheus](https://img.shields.io/badge/Prometheus-E6522C?style=for-the-badge\u0026logo=prometheus\u0026logoColor=white)\n![Grafana](https://img.shields.io/badge/Grafana-F46800?style=for-the-badge\u0026logo=grafana\u0026logoColor=white)\n![PostgreSQL](https://img.shields.io/badge/PostgreSQL-4169E1?style=for-the-badge\u0026logo=postgresql\u0026logoColor=white)\n\n\u003cbr/\u003e\n\n\u003e This project demonstrates a **complete DevOps lifecycle** — from local development  \n\u003e through automated security gates, GitOps deployment, and production monitoring.\n\n\u003c/div\u003e\n\n---\n\n## 📌 Table of Contents\n\n\u003cdetails\u003e\n\u003csummary\u003eClick to expand\u003c/summary\u003e\n\n- [About the Project](-#about-the-project)\n- [Architecture](-#-architecture)\n- [Tech Stack](-#-tech-stack)\n- [CI/CD Pipeline](-#-cicd-pipeline)\n- [GitOps Deployment](#-gitops-deployment)\n- [Monitoring \u0026 Alerting](#-monitoring--alerting)\n- [API Endpoints](#-api-endpoints)\n- [Getting Started Locally](#-getting-started-locally)\n- [Project Structure](#-project-structure)\n- [Security](#-security)\n- [Testing](#-testing)\n- [What I Learned](#-what-i-learned)\n\n\u003c/details\u003e\n\n---\n\n## 🏢 About the Project\n\nThe **Acquisitions API** is a backend platform for buying and selling SaaS businesses. It handles user authentication, business listing management, and deal tracking — but the real focus of this project is the **DevOps infrastructure** built around it.\n\nThis project was designed to answer one question: **what does a real production deployment pipeline actually look like?**\n\nThe answer involves five layers working together:\n\n1. **Application** — A Node.js REST API with JWT auth, role-based access control, Zod validation, and structured logging\n2. **CI** — GitHub Actions pipeline with security gates (Trivy + SonarCloud) that block bad code before it ships\n3. **CD** — ArgoCD doing GitOps: Git is the only source of truth, and any manual cluster changes are automatically reverted\n4. **Infrastructure** — Kubernetes with rolling deployments, health probes, and resource limits\n5. **Observability** — Prometheus scraping custom metrics, Grafana dashboards, and Alertmanager sending Slack notifications\n\n---\n\n## 🏗️ Architecture\n\n### CI/CD + GitOps Pipeline\n\n\u003cimg width=\"2242\" height=\"405\" alt=\"diagram-export-4-20-2026-5_52_01-PM\" src=\"https://github.com/user-attachments/assets/4617f3b6-5cba-47a8-b51a-d0d076b578a1\" /\u003e\n\u003cbr\u003e\n\n\u003e 🔗 **GitOps repo:** [acquisitions-gitops](https://github.com/himanshu2604/acquisitions-gitops)  \n\u003e All Kubernetes manifests and Helm values live there — not here.\n\n---\n\n## 🛠️ Tech Stack\n\n| Category           | Tool                          | Purpose                                    |\n| ------------------ | ----------------------------- | ------------------------------------------ |\n| **Runtime**        | Node.js 20 + Express          | Backend API framework                      |\n| **Language**       | TypeScript                    | Type safety across the codebase            |\n| **Database**       | Neon DB (Serverless Postgres) | Cloud-hosted PostgreSQL                    |\n| **ORM**            | Drizzle ORM                   | Type-safe queries and migrations           |\n| **Auth**           | JWT + bcrypt                  | Token-based auth and password hashing      |\n| **Validation**     | Zod                           | Request schema validation                  |\n| **Logging**        | Winston + Morgan              | Structured app logs + HTTP request logs    |\n| **Security**       | Helmet + CORS + Arcjet        | HTTP headers, cross-origin, bot protection |\n| **Metrics**        | prom-client                   | Exposes `/metrics` for Prometheus          |\n| **CI/CD**          | GitHub Actions                | Automated pipeline (lint → scan → deploy)  |\n| **Security Gates** | Trivy + SonarCloud            | CVE scanning + SAST code analysis          |\n| **Containers**     | Docker (multi-stage)          | Optimised production images                |\n| **Orchestration**  | Kubernetes                    | Scaling, rolling updates, self-healing     |\n| **GitOps**         | ArgoCD                        | Declarative CD with drift detection        |\n| **Monitoring**     | Prometheus + Grafana          | Metrics collection and dashboards          |\n| **Alerting**       | Alertmanager                  | Slack notifications on alert rules         |\n| **Testing**        | Jest + Supertest              | Unit and integration tests                 |\n| **Code Quality**   | ESLint + Prettier             | Linting and formatting                     |\n\n---\n\n## ⚙️ CI/CD Pipeline\n\nThe pipeline has **4 sequential stages**. Each stage must pass before the next one runs. If any stage fails, the image is never pushed to Docker Hub.\n\n\u003cimg width=\"1614\" height=\"637\" alt=\"diagram-export-4-20-2026-5_52_35-PM\" src=\"https://github.com/user-attachments/assets/2d00c311-ded5-40db-85d4-ed45d4b4f609\" /\u003e\n\u003cbr\u003e\n\n**Why two security gates?**\n\n- **Trivy** catches vulnerabilities in the OS packages and npm dependencies of your _built image_ — things you didn't write but ship with your app\n- **SonarCloud** catches security issues in _your own code_ — hardcoded secrets, SQL injection risks, insecure patterns\n\nNeither can catch what the other catches. You need both.\n\n---\n\n## 🔄 GitOps Deployment\n\nThis project uses a **two-repo GitOps pattern**:\n\n| Repo                           | Contains                                    | Who writes to it   |\n| ------------------------------ | ------------------------------------------- | ------------------ |\n| `acquisitions-api` (this repo) | Application code + Dockerfile + CI pipeline | Developers         |\n| `acquisitions-gitops`          | Kubernetes YAML manifests only              | CI bot (automated) |\n\n**Why separate repos?**\n\nWhen the CI pipeline passes all gates, it commits the new Docker image tag to `acquisitions-gitops`. ArgoCD watches that repo and deploys the change automatically.\n\nThis means:\n\n- **Git is always the source of truth** — not the CI runner, not someone's terminal\n- **Drift detection** — if anyone manually changes the cluster (e.g. scales down replicas), ArgoCD detects the divergence and automatically restores it within minutes\n- **Full audit trail** — every deployment is a Git commit with a message and author\n\n\u003e I tested drift detection by manually scaling the deployment to 1 replica.  \n\u003e ArgoCD restored it to 3 replicas automatically within 3 minutes.\n\n---\n\n## 📊 Monitoring \u0026 Alerting\n\n### How it works\n\nThe API exposes a `/metrics` endpoint via `prom-client`. Prometheus scrapes it every 15 seconds. Grafana visualises the data.\n\n### Custom metrics exposed\n\n| Metric                          | Type      | Description                                     |\n| ------------------------------- | --------- | ----------------------------------------------- |\n| `http_requests_total`           | Counter   | Every request labelled by method, route, status |\n| `http_request_duration_seconds` | Histogram | Request duration with percentile buckets        |\n| `process_heap_bytes`            | Gauge     | Node.js heap usage (auto-collected)             |\n| `nodejs_eventloop_lag_seconds`  | Gauge     | Event loop lag (auto-collected)                 |\n\n### Grafana dashboards\n\n| Dashboard                | ID      | Shows                            |\n| ------------------------ | ------- | -------------------------------- |\n| Kubernetes Cluster       | `315`   | Node CPU/memory, pod counts      |\n| ArgoCD Status            | `14584` | Sync health, deployment history  |\n| Node.js API              | `11159` | Request rate, heap, event loop   |\n| **Custom API dashboard** | —       | Req/s, P95 latency, error rate % |\n\n### Alert rules\n\n| Alert                         | Trigger                   | Severity |\n| ----------------------------- | ------------------------- | -------- |\n| `AcquisitionsPodCrashLooping` | Pod restarts \u003e 3 in 2 min | Critical |\n| `AcquisitionsHighCPU`         | CPU \u003e 80% for 5 min       | Warning  |\n| `AcquisitionsHighErrorRate`   | 5xx rate \u003e 5% for 2 min   | Critical |\n\nAlerts fire to **Slack** via Alertmanager.\n\n---\n\n## 📡 API Endpoints\n\n### Auth\n\n| Method | Endpoint            | Description                  | Auth     |\n| ------ | ------------------- | ---------------------------- | -------- |\n| `POST` | `/api/auth/signup`  | Register a new user          | Public   |\n| `POST` | `/api/auth/signin`  | Login and receive JWT cookie | Public   |\n| `POST` | `/api/auth/signout` | Logout and clear session     | Required |\n\n### Users\n\n| Method   | Endpoint         | Description         | Auth           |\n| -------- | ---------------- | ------------------- | -------------- |\n| `GET`    | `/api/users`     | Get all users       | Admin only     |\n| `GET`    | `/api/users/:id` | Get user by ID      | Required       |\n| `PUT`    | `/api/users/:id` | Update user profile | Owner or Admin |\n| `DELETE` | `/api/users/:id` | Delete user         | Admin only     |\n\n### Business Listings [Working on this feature !!!]\n\n| Method   | Endpoint            | Description             | Auth           |\n| -------- | ------------------- | ----------------------- | -------------- |\n| `GET`    | `/api/listings`     | Get all active listings | Public         |\n| `GET`    | `/api/listings/:id` | Get listing by ID       | Public         |\n| `POST`   | `/api/listings`     | Create new listing      | Required       |\n| `PUT`    | `/api/listings/:id` | Update listing          | Owner or Admin |\n| `DELETE` | `/api/listings/:id` | Delete listing          | Owner or Admin |\n\n### System\n\n| Method | Endpoint   | Description                               |\n| ------ | ---------- | ----------------------------------------- |\n| `GET`  | `/health`  | Health check — returns status + timestamp |\n| `GET`  | `/metrics` | Prometheus metrics endpoint               |\n\n---\n\n## 🚀 Getting Started Locally\n\n### Prerequisites\n\n- Node.js 20+\n- Docker + Docker Compose\n- A [Neon DB](https://neon.tech) account (free)\n- An [Arcjet](https://arcjet.com) account (free)\n\n### Setup\n\n```bash\n# 1. Clone the repo\ngit clone https://github.com/himanshu2604/acquisitions-api.git\ncd acquisitions-api\n\n# 2. Install dependencies\nnpm install\n\n# 3. Set up environment variables\ncp .env.example .env\n# Fill in all values in .env (see below)\n\n# 4. Run database migrations\nnpm run db:migrate\n\n# 5. Start the development server\nnpm run dev\n# API running at http://localhost:3000\n```\n\n### Environment Variables\n\n```env\nPORT=3000\nNODE_ENV=development\nDATABASE_URL=postgresql://user:pass@host/dbname\nJWT_SECRET=minimum_32_character_random_string\nARCJET_KEY=your_arcjet_api_key\nCOOKIE_SECRET=another_random_secret\n```\n\n### Using Docker Compose\n\n```bash\n# Start with Docker Compose (recommended)\ndocker-compose up\n\n# Or build and run the Docker image directly\ndocker build -t acquisitions-api .\ndocker run -p 3000:3000 --env-file .env acquisitions-api\n```\n\n---\n\n## 📁 Project Structure\n\n```\nacquisitions-api/\n├── src/\n│   ├── config/          # DB connection, app config\n│   ├── controllers/     # Route handlers (thin layer — calls services)\n│   ├── middleware/      # Auth, validation, rate limiting, metrics\n│   ├── models/          # Drizzle ORM schemas\n│   ├── routes/          # Express router definitions\n│   ├── services/        # Business logic\n│   ├── utils/           # Logger, JWT helpers, prom-client metrics\n│   └── validations/     # Zod request schemas\n├── tests/               # Jest + Supertest test suites\n├── .github/\n│   └── workflows/       # Full CI/CD pipeline\n│       ├── docker-and-trivy.yml\n│       ├── lint-and-format.yml\n│       └── tests.yml\n├── kubernetes/          # Local K8s reference manifests\n├── Dockerfile           # Multi-stage build\n├── docker-compose.yml   # Local development stack\n├── screenshot/          # Video + screenshots\n└── sonar-project.properties\n```\n\n---\n\n## 🔒 Security\n\n| Layer            | Tool                 | Protection                                      |\n| ---------------- | -------------------- | ----------------------------------------------- |\n| HTTP Headers     | Helmet               | XSS, clickjacking, MIME sniffing                |\n| Cross-Origin     | CORS                 | Controlled cross-origin access                  |\n| Bot Protection   | Arcjet               | Real-time bot detection and blocking            |\n| Rate Limiting    | Arcjet               | Per-route request throttling                    |\n| Authentication   | JWT + bcrypt         | Secure sessions, hashed passwords               |\n| Input Validation | Zod                  | All request bodies validated                    |\n| CVE Scanning     | Trivy (CI gate)      | Container vulnerabilities blocked before deploy |\n| Code Analysis    | SonarCloud (CI gate) | Security hotspots in source code                |\n| Container        | Non-root USER        | App runs as unprivileged user in Docker         |\n\n---\n\n## 🧪 Testing\n\n```bash\n# Run all tests\nnpm run test\n\n# Run with coverage report\nnpm run test -- --coverage\n\n# Run in watch mode\nnpm run test -- --watch\n```\n\nTests cover:\n\n- ✅ Auth flows — signup, signin, signout, invalid credentials\n- ✅ Protected route enforcement\n- ✅ Input validation error responses\n- ✅ User CRUD with role-based access\n- ✅ Business listings CRUD\n- ✅ Health check endpoint\n\n---\n\n## 💡 What I Learned\n\n\u003e These are the real lessons — not just \"I used Docker\".\n\n- **Why GitOps is safer than `kubectl` in a pipeline** — When a CI runner applies manifests directly, the cluster state lives in an ephemeral job. With GitOps, every change is a Git commit. The cluster always reflects what's in Git, and any deviation is automatically corrected.\n\n- **The difference between livenessProbe and readinessProbe** — Liveness restarts a container that is deadlocked. Readiness removes a pod from the load balancer during startup or when it's overwhelmed. You need both, and they serve completely different purposes.\n\n- **How Trivy catches what SonarCloud misses (and vice versa)** — Trivy finds CVEs in your dependencies and OS packages at the image level. SonarCloud finds insecure patterns in your own source code. Neither tool overlaps. Both are necessary.\n\n- **What drift detection actually means in practice** — It's not just a feature. It's a guarantee that your cluster state will always match your Git repo, even if someone panics and runs `kubectl` commands in production at 2am.\n\n- **How PromQL queries work** — `rate()` for per-second rates, `histogram_quantile()` for percentile latency, label selectors to filter by your app. Writing these from scratch is the difference between reading dashboards and building them.\n\n- **Why multi-stage Docker builds matter for security** — The builder stage installs dev dependencies and compiles TypeScript. The production stage copies only `dist/` and `node_modules`. The attack surface shrinks significantly because build tools never ship to production.\n\n---\n\n## 📄 License\n\nThis project is for educational and portfolio purposes.\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\n**🔗 Related repo:** [acquisitions-gitops](https://github.com/himanshu2604/acquisitions-gitops) — K8s manifests and Helm values\n\n_Built by Himanshu as a DevOps portfolio project_\n\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhimanshu2604%2Facquisitions-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhimanshu2604%2Facquisitions-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhimanshu2604%2Facquisitions-api/lists"}