Table of Contents
- The Challenge
- Architecture & Solution
- Monorepo Structure
- Tech Stack
- Key Engineering Decisions
- API Security Model
- Frontend Design System
- CI/CD Pipeline
- Deployment Topology
The Challenge
Most developer portfolios are static HTML pages or Notion exports — they show what you built but not *how* you build. The goal was to make the portfolio itself a living demonstration of engineering capability: a fully deployed, production-grade system with real security, real observability, and a real public API.
Requirements:
- Public portfolio website with premium aesthetics — not a Tailwind template clone
- Private admin panel for moderating public-submitted suggestions
- Secure REST API with JWT auth, CSRF protection, and rate limiting
- Full CI/CD pipeline with quality gates on every PR
- Single codebase, multiple deployable apps, shared type contracts
Architecture & Solution
Portfolio Nexus is a pnpm monorepo with three fully independent applications sharing TypeScript contracts through workspace packages.
Monorepo Structure
portfolio-nexus/
├── apps/
│ ├── website/ # Next.js 14 App Router — Public portfolio
│ ├── admin/ # Vite + React SPA — Private suggestion moderation
│ └── api/ # Express.js Modular Monolith
│
├── packages/
│ ├── types/ # Shared TypeScript contracts (single source of truth)
│ ├── content/ # Markdown case studies, blog posts, project metadata
│ └── config/ # Spider-web scores, project status configs
│
├── infra/ # Dockerfiles + docker-compose.prod.yml (Traefik)
├── .github/workflows/ # CI quality gate
└── *.sh # setup, start, stop, dev, deploy scriptsBoundary enforcement: apps/* cannot import from other apps/*. All shared contracts flow through packages/* only. Enforced by ESLint boundaries plugin in CI.
Tech Stack
| Layer | Technology | Role |
|---|---|---|
| Public Website | Next.js 14 App Router | SSG + ISR, Server/Client Components |
| Admin Panel | Vite 5 + React 18 | Private SPA, no SSR needed |
| API | Express.js 4 + TypeScript | Modular monolith REST API |
| Database | PostgreSQL 16 + Prisma | Schema, migrations, type-safe queries |
| Auth | JWT in httpOnly cookie | 8-hour sessions, SameSite=Strict |
| State (Admin) | Zustand | Lightweight client state |
| Styling | CSS Modules + CSS Custom Properties | No Tailwind, no inline styles |
| Animations | Framer Motion | Scroll entrances, hero aurora |
| Logger | Pino | JSON-structured, redacts auth headers |
| Validation | Zod | Schema-first, types inferred from schemas |
| Testing | Vitest + Supertest | Unit + E2E API contract tests |
| Package Manager | pnpm workspaces | Single lockfile, workspace protocol |
| Containerization | Docker multi-stage | deps → builder → runner (minimal image) |
| Reverse Proxy | Traefik + Let's Encrypt | Automatic HTTPS, zero-downtime routing |
| CDN | Vercel | Edge SSG serving for public website |
Key Engineering Decisions
1. SSG + ISR over Full SSR
The portfolio content (case studies, blog posts) is static by nature. Next.js App Router Server Components read markdown files from packages/content/ at build time using fs.readFileSync — zero runtime API cost, maximum Lighthouse scores, instant page loads globally from Vercel CDN.
Only dynamic content (GitHub contribution heatmap) uses ISR with a 1-hour revalidation window.
2. No TailwindCSS — CSS Modules + Design Tokens
The decision to use CSS Modules with CSS custom properties (not Tailwind) means:
- The portfolio looks like no other portfolio (custom, not a template)
- Zero utility class sprawl in JSX
- Design tokens are a single source of truth in
tokens.css prefers-reduced-motionand dark mode handled cleanly at the CSS layer
3. API Modular Monolith
The API follows the same modular monolith pattern as Aatmanova — each domain lives in a vertical slice that passes the deletion test. Clean architecture layers are enforced:
Router (Express-only) → Controller (parse/respond) → Service (orchestrate)
→ Policy (pure rules, no I/O) → Repository (data only) → PrismaNo circular dependencies enforced by madge in CI.
4. CSRF Double-Submit Pattern
Admin routes use two independent CSRF defenses simultaneously:
SameSite=Strictcookie — browser won't send cookie on cross-origin requestsX-Admin-Origin: portfolio-admincustom header — cannot be set by cross-origin forms, only JavaScript (which must already be on the same origin)
Together these make CSRF attacks impossible without compromising the admin origin itself.
5. Manual DI Container
No magic, no decorators, no IoC framework. The entire dependency graph is visible in one file:
// container.ts — readable dependency wiring
const suggestionsRepo = new SuggestionsRepository(prisma);
const suggestionsPolicy = new SuggestionsPolicy();
const suggestionsService = new SuggestionsService(
suggestionsRepo, suggestionsPolicy, emailService, logger
);
export const container = {
suggestionsController: new SuggestionsController(suggestionsService),
};API Security Model
| Control | Implementation |
|---|---|
| Auth | JWT in httpOnly, secure, SameSite=Strict cookie (8h TTL) |
| CSRF | Double-submit: SameSite=Strict + X-Admin-Origin header |
| Rate Limiting | 3 suggestions/hour/IP (in-memory Map, v1) |
| Input Validation | Zod on every endpoint — body, query params, route params |
| Security Headers | Helmet.js (11 headers including CSP, HSTS, X-Frame-Options) |
| CORS | Explicit origin allowlist — never * in production |
| Logging | Pino — redacts req.headers.authorization, body.password, body.token |
| Env Validation | Zod-validated env.ts — fails fast on startup with missing vars |
Frontend Design System
The public website implements the Linear/Vercel aesthetic — dark canvas, precision borders, accent glow — built entirely from scratch with CSS custom properties:
:root {
--color-background: #0A0A0A; /* True black canvas */
--color-surface: #111111; /* Card background */
--color-accent: #3B82F6; /* Blue — primary CTAs */
--color-text: #FAFAFA; /* Near-white primary */
--color-text-muted: #A3A3A3; /* Secondary copy */
--font-display: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-body: 'DM Sans', system-ui, sans-serif;
--font-mono: 'Fira Code', 'Courier New', monospace;
}Animation rules (from Framer Motion centralized config):
- Entrance:
fadeUp(0–24px, opacity 0→1, 0.6s spring easing) - Hero: Aurora mesh gradient (
conic-gradient,hue-rotateCSS animation) - Hover:
translateY(-2px)+ glowbox-shadow, 200ms useReducedMotion()disables all JS animations for accessibility
WCAG AA compliance on all text — targeting AAA 7:1 for body copy in dark mode.
CI/CD Pipeline
GitHub Actions runs on every PR to main or develop:
# quality.yml — runs on every PR
steps:
- TypeScript: pnpm -r tsc --noEmit # All workspaces
- Lint: pnpm -r lint
- Tests: pnpm --filter api test:unit
- Circular: npx madge --circular apps/api/src
- Build: pnpm --filter website build # Validates SSG
- Build: pnpm --filter admin build
- Audit: npm audit --audit-level=highNo merge to main without all checks green.
Deployment Topology
Vercel (Next.js SSG — public website)
├── Global CDN edge nodes
├── Automatic builds on git push
└── ISR for GitHub heatmap (1h TTL)
VPS (Ubuntu 22.04)
├── Traefik (reverse proxy + Let's Encrypt)
│ ├── api-portfolio.aatmanova.in → portfolio-api container (port 4000)
│ └── access.aatmanova.in → Nginx serving Vite admin build
├── PM2 (Express API process management)
│ ├── 1 instance, fork mode
│ ├── max_memory_restart: 512M
│ └── zero-downtime: pm2 reload --update-env
└── PostgreSQL 16 (Docker container, no exposed ports)Deploy to VPS with a single command:
./deploy.sh "feat: add project case study"
# → Tests → git push → rsync → Docker rebuild → migration → health checkEngineering Proof
Real-world validation, system demonstrations, and interface captures of the execution states.
System Captures
Full Page Web Captures
Scrollable web preview simulations. Hover or scroll to preview the entire page. Use the maximize trigger to view the full resolution capture.


Architecture Feedback
Spotted a potential optimization or antipattern? Let me know.
