A full-stack financial portfolio and stock tracking platform — built with a production-grade monorepo architecture featuring a dedicated backend API, React frontend, observability stack, Docker-based deployment, and automated CI/CD pipelines.
- Overview
- System Architecture
- Data Flow
- CI/CD Pipeline
- Tech Stack
- Project Structure
- Prerequisites
- Environment Configuration
- Installation
- Running the Application
- Makefile Commands
- API Reference
- Monitoring
- Deployment
- Contributing
- License
Vaultlink is a monorepo platform for tracking financial portfolios, managing stock watchlists, and monitoring asset performance. The system is structured into three independent layers — a Node.js backend API, a React frontend, and a Grafana-based observability stack — each deployable in isolation but orchestrated together via Docker Compose.
Key design decisions:
- Monorepo structure: Backend, frontend, and monitoring live in one repository with shared environment configuration, making cross-layer changes atomic and reviewable in a single pull request.
- Docker Compose for dev and prod: Separate
docker-compose.ymlanddocker-compose.prod.ymlfiles allow identical local development environments and reproducible production deployments. - Makefile automation: All common developer workflows (start, stop, build, test, lint) are encoded as
maketargets, eliminating onboarding friction. - Ticker validation: Stock ticker symbols are validated server-side before any external API call, preventing unnecessary third-party quota usage.
- Grafana observability: The monitoring stack ships with a pre-configured Grafana dashboard for tracking API latency, error rates, and system health metrics.
graph TB
USER([Browser Client])
subgraph VERCEL [Vercel Edge Network]
direction TB
FE[React 18 + Vite SPA]
AXIOS[Axios HTTP Client]
FE --> AXIOS
end
subgraph DOCKER [Docker Compose Orchestrated Services]
direction TB
subgraph API_LAYER [Backend Node.js + Express]
direction LR
GW[API Gateway :5000]
AUTH[JWT Auth Middleware]
VAL[Ticker Validator]
CTRL[Controllers]
SVC[Service Layer]
GW --> AUTH --> VAL --> CTRL --> SVC
end
subgraph DB_LAYER [Data Layer]
DB[(Primary Database)]
end
subgraph OBS_LAYER [Observability Stack]
direction LR
PROM[Prometheus :9090]
GRAF[Grafana :3000]
PROM --> GRAF
end
end
subgraph EXTERNAL [External Services]
STOCK[Stock Market Data API]
GH[GitHub Actions CI/CD]
end
USER -->|HTTPS| FE
AXIOS -->|REST /api/v1| GW
SVC -->|Fetch enriched quotes| STOCK
SVC -->|Read and Write| DB
DB -->|Query results| SVC
API_LAYER -->|Scrape /metrics endpoint| PROM
GH -->|Deploy on push to main| VERCEL
GH -->|Build and run tests| DOCKER
sequenceDiagram
actor User
participant FE as React Frontend
participant GW as API Gateway
participant AUTH as JWT Middleware
participant VAL as Ticker Validator
participant SVC as Stock Service
participant EXT as Market Data API
participant DB as Database
participant PROM as Prometheus
User->>FE: Enter ticker symbol e.g. AAPL
FE->>GW: POST /api/v1/watchlist with symbol AAPL
GW->>AUTH: Verify Authorization Bearer token
AUTH-->>GW: Decoded userId
GW->>VAL: Validate symbol format and whitelist
alt Symbol invalid
VAL-->>GW: 400 Bad Request
GW-->>FE: Error - invalid ticker symbol
else Symbol valid
VAL-->>GW: Passed
end
GW->>DB: SELECT watchlist WHERE userId AND symbol = AAPL
DB-->>GW: Row count = 0, not a duplicate
GW->>SVC: enrichTicker AAPL
SVC->>EXT: GET /quote?symbol=AAPL
EXT-->>SVC: price, volume, change, marketCap
SVC-->>GW: Enriched ticker payload
GW->>DB: INSERT INTO watchlist userId, symbol, metadata
DB-->>GW: 201 Created
GW->>PROM: Increment watchlist_add_total counter
GW-->>FE: 201 with symbol, price, change, volume
FE-->>User: Ticker card appears on dashboard with live data
flowchart TD
DEV([Developer pushes to main])
DEV --> GHA[GitHub Actions Workflow Triggered]
GHA --> BE_JOB[Backend Job]
GHA --> FE_JOB[Frontend Job]
subgraph BE_PIPELINE [Backend Pipeline]
direction TB
BE1[Checkout code]
BE2[npm ci - install dependencies]
BE3[Run ticker validation tests]
BE4[Build Docker image]
BE5[Push image to registry]
BE6[SSH deploy to server - docker compose pull and up]
BE1 --> BE2 --> BE3 --> BE4 --> BE5 --> BE6
end
subgraph FE_PIPELINE [Frontend Pipeline]
direction TB
FE1[Checkout code]
FE2[npm ci - install dependencies]
FE3[ESLint static analysis]
FE4[Vite production build]
FE5[Deploy to Vercel Edge CDN]
FE1 --> FE2 --> FE3 --> FE4 --> FE5
end
BE_JOB --> BE_PIPELINE
FE_JOB --> FE_PIPELINE
BE3 -->|Test failure| FAIL([Pipeline blocked - merge prevented])
FE3 -->|Lint failure| FAIL
BE6 --> LIVE([Production live - both services updated])
FE5 --> LIVE
| Technology | Purpose |
|---|---|
| React | UI component library |
| Vite | Build tool and dev server |
| Axios | HTTP client for API communication |
| Tailwind CSS | Utility-first styling |
| Technology | Purpose |
|---|---|
| Node.js | JavaScript runtime |
| Express | REST API framework |
| JWT | Stateless authentication |
| bcrypt | Password hashing |
| Technology | Purpose |
|---|---|
| Docker + Docker Compose | Container orchestration for dev and prod |
| GitHub Actions | CI/CD pipelines |
| Vercel | Frontend hosting and edge deployment |
| Grafana | Observability and metrics dashboards |
| Prometheus | Metrics collection and storage |
| Makefile | Developer workflow automation |
Vaultlink/
|
+-- .github/
| +-- workflows/
| +-- ci.yml # Backend test and build pipeline
| +-- deploy-frontend.yml # Vercel frontend deploy pipeline
|
+-- backend/ # Node.js Express API
| +-- src/
| | +-- controllers/ # Route handlers
| | +-- services/ # Business logic + external API calls
| | +-- models/ # Database schemas
| | +-- middleware/ # Auth, validation, error handling
| | +-- routes/ # API route definitions
| | +-- utils/ # Ticker validator, helpers
| +-- tests/ # Test suite - ticker validation etc.
| +-- package.json
|
+-- frontend/ # React + Vite SPA
| +-- src/
| | +-- components/ # Reusable UI components
| | +-- pages/ # Route-level page components
| | +-- hooks/ # Custom React hooks
| | +-- services/ # Axios API wrappers
| | +-- context/ # Global state
| +-- vite.config.js
| +-- package.json
|
+-- monitoring/ # Observability stack
| +-- grafana/
| | +-- dashboards/ # Pre-configured Grafana dashboard JSON
| | +-- provisioning/ # Grafana datasource config
| +-- prometheus/
| +-- prometheus.yml # Scrape config pointing to backend
|
+-- .env.example # Environment variable template
+-- docker-compose.yml # Development stack
+-- docker-compose.prod.yml # Production stack
+-- Makefile # Developer automation commands
+-- LICENSE
+-- README.md
| Requirement | Version | Notes |
|---|---|---|
| Node.js | v18+ | nodejs.org |
| Docker | Latest | docker.com |
| Docker Compose | v2+ | Bundled with Docker Desktop |
| Make | Any | Pre-installed on Linux/Mac. Windows: use WSL or Chocolatey |
cp .env.example .env# ── Server ────────────────────────────────────────────────────────────────────
PORT=5000
NODE_ENV=development
# ── Database ──────────────────────────────────────────────────────────────────
DATABASE_URL=your_database_connection_string
# ── Authentication ────────────────────────────────────────────────────────────
JWT_SECRET=your-minimum-64-character-secret
JWT_EXPIRES_IN=7d
# ── Stock Market API ──────────────────────────────────────────────────────────
STOCK_API_KEY=your_api_key
STOCK_API_BASE_URL=https://api.yourprovider.com
# ── Frontend ──────────────────────────────────────────────────────────────────
VITE_API_URL=http://localhost:5000
# ── Monitoring ────────────────────────────────────────────────────────────────
GRAFANA_ADMIN_PASSWORD=changemegit clone https://github.com/ALTIQUUM/Vaultlink.git
cd Vaultlink
cp .env.example .env
# Edit .env with your credentials# Development
make dev
# or
docker compose up
# Production
make prod
# or
docker compose -f docker-compose.prod.yml up -d# Backend
cd backend && npm install && npm run dev
# Frontend (new terminal)
cd frontend && npm install && npm run dev| Service | URL |
|---|---|
| Frontend | http://localhost:5173 |
| Backend API | http://localhost:5000 |
| Grafana Dashboard | http://localhost:3000 |
| Prometheus | http://localhost:9090 |
make dev # Start full development stack via Docker Compose
make prod # Start production stack
make stop # Stop all running containers
make build # Rebuild all Docker images
make test # Run backend test suite
make lint # Run ESLint on frontend
make logs # Tail logs from all containers
make clean # Remove containers, volumes, and build artifactsAll endpoints prefixed with /api/v1. Protected routes require:
Authorization: Bearer <jwt_token>
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/api/v1/auth/register |
No | Create account |
POST |
/api/v1/auth/login |
No | Login, returns JWT |
GET |
/api/v1/auth/me |
Yes | Current user |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/v1/watchlist |
Yes | Get user watchlist with live prices |
POST |
/api/v1/watchlist |
Yes | Add ticker to watchlist |
DELETE |
/api/v1/watchlist/:symbol |
Yes | Remove ticker |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/v1/stocks/:symbol |
Yes | Get current price and metadata |
GET |
/api/v1/stocks/:symbol/history |
Yes | Historical price data |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/v1/health |
Server and DB status |
The monitoring stack runs as part of the Docker Compose setup. Grafana is pre-configured with a dashboard covering:
- API request rate and latency (p50, p95, p99)
- Error rate by endpoint
- Database query performance
- Container CPU and memory usage
Access Grafana at http://localhost:3000 with the credentials set in GRAFANA_ADMIN_PASSWORD.
The .github/workflows/deploy-frontend.yml pipeline automatically deploys the React app to Vercel on every push to main. Set the following secrets in your GitHub repository settings:
VERCEL_TOKEN
VERCEL_ORG_ID
VERCEL_PROJECT_ID
Use docker-compose.prod.yml on your server:
docker compose -f docker-compose.prod.yml up -dPair with Nginx as a reverse proxy for HTTPS termination.
-
Branch from
main:git checkout -b feat/your-feature
-
Follow Conventional Commits:
feat(backend): add portfolio performance endpoint fix(frontend): correct ticker symbol normalisation chore(docker): update compose healthcheck intervals -
Ensure tests pass before opening a PR:
make test
MIT License. See LICENSE for details.
ALTIQUUM / Vaultlink — Built in silence. Found in history ®