GitHub Case Study

SinooHub - Portfolio Platform

Portfolio logo

Full-Stack Developer Portfolio

A production-oriented portfolio with a public resume, authenticated CMS, GitHub-powered case studies, and a recruiter contact workflow.

Live Portfolio API Health

Next.js 16 React 19 TypeScript Tailwind CSS NestJS Prisma Supabase



Table of contents


✨ Overview

This monorepo powers an English-language portfolio for a Full-Stack Developer Intern. Content is stored as structured JSON rather than raw HTML, so the public resume, live editor preview, print view, and version history all share the same validated data model.

The project is deliberately split into a Next.js frontend and a NestJS API. Supabase provides authentication and PostgreSQL; Prisma owns database access. Public project case studies are loaded server-side from configured public GitHub repositories, which prevents browser CORS issues and keeps GitHub retrieval isolated from the UI.

πŸš€ Key features

  • Public portfolio and resume β€” responsive home page, printable resume, and public resume URLs by slug.
  • Authenticated resume CMS β€” Supabase Bearer-token authentication with an ADMIN_EMAILS allowlist; create, duplicate, edit, publish, select primary, and delete resume profiles.
  • Versioned publishing β€” every publish creates a ResumeVersion snapshot; an admin can inspect history and roll back safely.
  • Structured content β€” React Hook Form and Zod validate resume JSON before it is rendered by the frontend.
  • GitHub-backed project case studies β€” multi-repository selection; server-side GitHub API lookup with raw-content fallbacks for main, master, and HEAD README files.
  • Safe Markdown rendering β€” GitHub Flavored Markdown, syntax highlighting, Mermaid diagrams, responsive images, inline badges, and repository-relative asset URLs.
  • Contact and JD intake β€” a visitor can send an email, a message, a JD URL, or a JD file; the API validates input, applies anti-spam protections, and persists the request.
  • Production deployment β€” frontend on Vercel; NestJS API on Render with a health check and Prisma migration at startup.

πŸ—οΈ Architecture

Rendering Architecture Diagram...

Case-study request flow

Rendering Architecture Diagram...

The case-study feature follows a small Clean Architecture boundary: the application service depends on repository and README-client interfaces, while Prisma and GitHub HTTP implementations live in infrastructure. This makes the selection logic straightforward to unit test without a database or network request.

🧰 Technology

AreaTechnologies
FrontendNext.js 16, React 19, TypeScript, Tailwind CSS
Forms and validationReact Hook Form, Zod
Markdown and diagramsreact-markdown, remark-gfm, rehype-highlight, Mermaid
BackendNestJS 11, TypeScript, REST
DataSupabase PostgreSQL, Prisma 7
AuthenticationSupabase Auth and a NestJS guard
Toolingpnpm workspace, ESLint, Prettier, Jest
HostingVercel, Render, Supabase

πŸ“ Repository structure

portfolio/
β”œβ”€β”€ frontend/                         # Next.js App Router application
β”‚   └── src/
β”‚       β”œβ”€β”€ app/                      # Public, resume, project, and admin routes
β”‚       β”œβ”€β”€ features/
β”‚       β”‚   β”œβ”€β”€ home/                 # Landing-page contact workflow
β”‚       β”‚   β”œβ”€β”€ projects/             # GitHub README case-study renderer
β”‚       β”‚   └── resume/               # Public resume and editor UI
β”‚       └── lib/                      # Supabase, image URL, and UI helpers
β”œβ”€β”€ backend/                          # NestJS API
β”‚   β”œβ”€β”€ prisma/                       # Database schema and migrations
β”‚   └── src/
β”‚       β”œβ”€β”€ auth/                     # Supabase guard and authenticated request type
β”‚       β”œβ”€β”€ contact/                  # Contact/JD endpoint
β”‚       β”œβ”€β”€ prisma/                   # Prisma service and module
β”‚       └── resume/
β”‚           β”œβ”€β”€ application/          # Project case-study use case + tests
β”‚           β”œβ”€β”€ domain/               # Dependency ports and contracts
β”‚           β”œβ”€β”€ infrastructure/       # Prisma repository and GitHub README client
β”‚           └── dto/                  # Request validation DTOs
β”œβ”€β”€ render.yaml                       # Render API deployment definition
β”œβ”€β”€ start-dev.bat                     # Windows development helper
└── pnpm-workspace.yaml               # Workspace packages

🏁 Quick start

Prerequisites

  • Node.js 22.14+
  • pnpm 11.13+
  • A Supabase project with PostgreSQL and Auth enabled

Install and configure

git clone <your-repository-url>
cd portfolio
pnpm install

# Create these files from the examples in the repository.
cp backend/.env.example backend/.env
cp frontend/.env.example frontend/.env.local

Set the required values, generate Prisma Client, then start both applications:

pnpm --filter backend db:generate
pnpm dev

The frontend runs at http://localhost:3000; the API health endpoint is http://localhost:3001/api/health.

On Windows, start-dev.bat runs the same workspace development command.

πŸ” Environment variables

Backend β€” backend/.env

VariablePurpose
DATABASE_URLSupabase PostgreSQL pooled connection URL used at runtime
DIRECT_URLDirect PostgreSQL URL used by Prisma migrations
CORS_ORIGINAllowed frontend origin, for example http://localhost:3000
SUPABASE_URLSupabase project URL
SUPABASE_PUBLISHABLE_KEYSupabase publishable/anon key used to validate sessions
PORTFOLIO_OWNER_IDSupabase Auth UID that exclusively owns the public portfolio CV
ADMIN_EMAILSComma-separated email allowlist for CMS access

Frontend β€” frontend/.env.local

VariablePurpose
NEXT_PUBLIC_API_URLAPI base URL; defaults to http://localhost:3001/api
NEXT_PUBLIC_SUPABASE_URLSupabase project URL available to the browser client
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEYBrowser-safe Supabase publishable/anon key

Never commit real credentials, service-role keys, or production database URLs.

πŸ”Œ API overview

Public routes

MethodRoutePurpose
GET/api/healthAPI health status
GET/api/resumePrimary published resume
GET/api/resume/:slugPublished resume by slug
GET/api/projects/:slug/case-study?repo=<index-or-label>Markdown-backed project case study
POST/api/contactContact message and optional JD submission

Admin routes

These routes require a valid Supabase Bearer token and an email in ADMIN_EMAILS.

MethodRoutePurpose
GET / POST/api/admin/resumesList or create/duplicate profiles
GET / PUT / DELETE/api/admin/resumes/:idRead, save, or delete a profile
PUT/api/admin/resumes/:id/metaUpdate title and slug
POST/api/admin/resumes/:id/publishPublish and create a version snapshot
POST/api/admin/resumes/:id/set-primarySet the primary profile
GET/api/admin/resumes/:id/versionsView version history
POST/api/admin/resumes/:id/rollback/:versionIdRestore a version

Backward-compatible primary-resume endpoints remain under /api/admin/resume.

βœ… Quality checks

# Backend
pnpm --filter backend exec eslint "src/**/*.ts"
pnpm --filter backend exec jest --runInBand
pnpm --filter backend run build

# Frontend
pnpm --filter frontend run lint
pnpm --filter frontend run build

☁️ Deployment

  • Vercel: deploy the frontend workspace and set the three NEXT_PUBLIC_* variables.
  • Render: use render.yaml. Its build generates Prisma Client and builds the backend; startup runs prisma migrate deploy before launching NestJS.
  • Supabase: provide the PostgreSQL URLs, configure Auth, and add the production Vercel URL to CORS_ORIGIN.

πŸ—ΊοΈ Roadmap

  • Additional resume templates and managed project content
  • Accessibility, SEO, sitemap, and dark/light mode polish
  • Independent CI workflow for lint, test, and build
  • Blog/newsletter and portfolio search
  • RAG assistant, Telegram handoff, and live chat via SSE

Inspired by the clear product-first documentation style of SafeNews AI Crawler Engine and SafeNews Mobile App.

SinooHub - Portfolio Platform | Nguyen Tran Anh Khoa