A full-stack web application demonstrating modern DevOps practices: CI/CD pipelines, automated testing, dependency management, and cloud deployment.
- Architecture
- Tech Stack
- Project Structure
- Installation
- Running the Application
- Testing
- CI/CD Workflows
- Deployment
- Design Decisions
- Challenges
- License
βββββββββββββββββββββββββββββββββββββββββββββββ
β GitHub Repository β
β β
β βββββββββββ ββββββββββββββββββββ β
β β client/ β β server/ β β
β β (React) βββββββββΊβ (Express API) β β
β βββββββββββ HTTP ββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββ β
β β .github/workflows/ β β
β β ci.yml integration.yml e2e.yml β β
β β deploy.yml β β
β βββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββ
β β
(Render CDN) (EC2 / Render)
frontend backend
| Component | Technology | Responsibility |
|---|---|---|
| Frontend | React 18 + Vite | UI, user interaction, API calls |
| Backend | Node.js + Express | REST API, business logic |
| CI/CD | GitHub Actions | Lint, test, build, deploy |
| E2E | Playwright | Full user-flow simulation |
| Deployment | Render / AWS EC2 | Hosting the production app |
- React 18 β component-based UI library
- Vite β fast build tool and dev server with HMR
- Vitest + React Testing Library β unit and component tests
- Playwright β end-to-end tests
- ESLint β static analysis; Prettier β code formatting
- Node.js + Express β REST API server
- CORS β cross-origin request support
- dotenv β environment variable management
- Jest + Supertest β unit and integration tests
- ESLint β static analysis
- GitHub Actions β automated pipelines (CI, integration, E2E, deploy)
- Dependabot β automated dependency update PRs
- Render β PaaS hosting (zero-config deploys)
- AWS EC2 β IaaS target for SSH-based deployment
- PM2 β Node.js process manager on EC2
Dev-ops/
βββ .github/
β βββ dependabot.yml # Automated dependency update config
β βββ workflows/
β βββ ci.yml # Lint + test + build on every push/PR
β βββ integration.yml # Multi-version integration tests
β βββ e2e.yml # Playwright end-to-end tests
β βββ deploy.yml # SSH-based EC2 deployment
βββ client/ # React frontend
β βββ src/
β β βββ App.jsx # Root component (health status display)
β β βββ App.test.jsx # Vitest component tests
β β βββ main.jsx # ReactDOM entry point
β β βββ index.css # Global styles
β β βββ setupTests.js # @testing-library/jest-dom setup
β βββ index.html
β βββ vite.config.js # Vite + Vitest configuration
β βββ package.json
βββ server/ # Express backend
β βββ src/
β β βββ app.js # Express app (routes, middleware)
β β βββ index.js # Server entry point (port, dotenv)
β βββ tests/
β β βββ app.test.js # Unit tests for API endpoints
β β βββ integration.test.js # HTTP integration tests
β βββ eslint.config.mjs
β βββ package.json
βββ e2e/ # Playwright E2E tests
β βββ tests/
β β βββ app.spec.js # Full user-flow scenarios
β βββ playwright.config.js
β βββ package.json
βββ scripts/
β βββ setup.sh # Idempotent local setup script
βββ .prettierrc # Shared Prettier config
βββ render.yaml # Render.com deployment spec
βββ README.md
- Node.js β₯ 18
- npm β₯ 9
bash scripts/setup.shThe script is idempotent: you can run it as many times as you like β it skips steps that are already done (e.g., it won't overwrite existing .env files).
# Install root dependencies
npm install
# Install client dependencies
cd client && npm install && cd ..
# Install server dependencies
cd server && npm install && cd ..# Start backend (port 5001)
cd server && npm run dev
# Start frontend (port 5173, proxies /api β backend)
cd client && npm run devOpen http://localhost:5173 in your browser.
# Backend unit tests (Jest)
cd server && npm test
# Frontend component tests (Vitest)
cd client && npm testThe backend integration tests (server/tests/integration.test.js) verify:
- HTTP status codes for all endpoints
- JSON response structure and content-type headers
- CORS headers are present
- 404 handling for unknown routes
cd server && npm testE2E tests simulate real user interactions across the full stack:
- Page renders with expected headings
- Backend health data loads and displays
- Direct API assertions via Playwright's
requestfixture
cd e2e
npm install
npx playwright install chromium
npm test# Frontend
cd client && npm run lint
# Backend
cd server && npm run lint| Workflow | Trigger | What it does |
|---|---|---|
CI Pipeline (ci.yml) |
push / PR to main |
Lint β Test β Build (client & server) |
Integration (integration.yml) |
push / PR to main |
Matrix test on Node 18, 20, 22 |
E2E Tests (e2e.yml) |
push / PR to main |
Playwright browser tests |
Deploy to EC2 (deploy.yml) |
push to main |
SSH deploy, PM2 reload |
Every pull request must pass lint checks before it can be merged.
Defined in render.yaml:
- Backend (
dev-ops-backend) β Node.js web service, starts withnode src/index.js - Frontend (
dev-ops-frontend) β Static site, built withvite build
Add the following repository secrets:
| Secret | Description |
|---|---|
EC2_HOST |
Public IP or DNS of your EC2 instance |
EC2_USER |
SSH username (e.g. ubuntu, ec2-user) |
EC2_SSH_KEY |
Private SSH key (PEM format) |
The deploy.yml workflow SSH-es into the instance, pulls the latest code, installs dependencies, and performs a zero-downtime reload with PM2.
- Monorepo layout β
client/andserver/in one repository to keep CI simple and changes atomic. - Vite over CRA β faster HMR, native ESM, and built-in Vitest integration.
- Supertest for API tests β mounts Express in-process; no listening port needed, making tests fast and deterministic.
- Playwright for E2E β cross-browser, API-testing built-in, and first-class CI support.
- Idempotent scripts β
scripts/setup.shuses guards ([ ! -f ],mkdir -p) so re-running never corrupts state. - Dependabot β automated weekly PRs for npm and Actions updates reduce manual maintenance burden.
- PM2 on EC2 β process manager with automatic restart on crash and
pm2 savefor reboot persistence.
- ESLint version mismatch β client uses ESLint 8 (with
--extflag), server uses ESLint 10 flat config. Each directory therefore carries its own config. - Vitest watch mode in CI β
vitestdefaults to watch mode; the CI workflow passes-- --runto force a single-run exit. - CORS in E2E β Playwright's
requestfixture bypasses the browser, so CORS headers are not enforced there; browser tests cover the real CORS flow.