Back to Projects

Art Portfolio Platform

2023.09 - 2024.05
Full-Stack Engineer & Architecture Lead
TL;DR

Migrated from Vue SPA to Next.js SSG/ISR. SEO improved from 72 → 100, performance score 94/100. Three-tier caching + CDN delivering blazing fast loading speeds.

FCP 2.1s → 0.8s (-62%), LCP 3.8s → 2.1s (-45%) (migration phase); final Perf 94/100, SEO 100/100.

⬇️

API calls 12 → 2.6/visit (-78%), CDN hit rate 90%.

技术栈
Next.js 14 App Router / SSG+ISRStrapi v5 CMSSupabase EU (Postgres+Storage)GDPR-friendly (Umami)
Art Portfolio Platform

Background & Goals

  • Legacy site: Vue SPA with SEO-unfriendly architecture, slow first paint, no caching; international access relied on direct browser connections, large images, high CLS.
  • Goals: Search visibility (SSR/SSG/ISR), performance (Core Web Vitals all green), stable editing experience (CMS), multilingual (EN/FR/ZH), and European compliance (GDPR-friendly analytics).

Architecture

Architecture flow: Next.js (Vercel) → Strapi v5 API → Supabase (Postgres + Storage); frontend images via Next/Image + Supabase CDN; Umami Edge collects minimal data

Loading diagram...

Technical Highlights:

  • Frontend Next.js → Strapi API → Supabase Postgres/Storage; frontend images via Next/Image + Supabase CDN; Umami Edge collects minimal data.
  • Public content requires zero authentication + database RLS as fallback (even bypassing API only reads published content); Admin side uses JWT.
  • Transport layer (TLS 1.3, HSTS), domains and certificates managed by Vercel/Render/Supabase.

Results & Metrics

指标数值
FCP
-62% (migration phase)
2.1s → 0.8s
LCP
-45% (migration phase)
3.8s → 2.1s
TTI
-55%
4.2s → 1.9s
CLS
-80%
0.15 → 0.03
TBT
-46%
280ms → 150ms
Bundle Size
-43%
1.2MB → 680KB
Requests/Visit
-78%
12 → 2.6
CDN Hit Rate
production verified
~90%
SEO Score
Google indexed 0 → 320 pages, 1,240 organic monthly visits
72 → 100
Perf Score
Lighthouse (final)
94/100
Email Success Rate
subscription conversion +334%, zero churn
23% → 99.7%
API Calls
backend load -78%, cost savings $15/mo
12 → 2.6 calls

API Contract

  • Public API: Read artworks/categories/homepage; no token required (relies on RLS: read-only published content).
  • Admin API: JWT (Strapi Admin), 24h expiration, no refresh token.
  • RLS example (read-only published): USING (published_at IS NOT NULL); media files public read policy.
  • Rate limiting: IP-level rate limiting on public endpoints.

Data Model

Loading diagram...
  • Core ER: categories ⇄ products (artworks) ⇄ files (media);
  • Strapi v5 Document Model:
    Same document_id → Draft (published_at=NULL) + Published (published_at!=NULL)
    Frontend reads only Published (zero auth + RLS fallback)
  • Technical Highlights:
    Admin edits Draft → Click Publish → Auto-sync to Published
    Database RLS Policy enforced: Even bypassing API only reads published content

Key Code & Engineering Practices

Subscription Service (Idempotency & State Machine)

Migrated from SMTP to Mailgun HTTP API; state machine pending → verified → unsubscribed + idempotency handling; delivery success rate reached ≈99.7%.

services/subscriptionService.ts

Production Data Migration Script

SQLite → Supabase (Postgres + Storage), four phases: "pre-check → compatibility → atomic migration → validation", media file concurrency optimization: serial upload 2h → concurrent upload 20min (83% improvement). Total migration time: 2.5 hours (319 products + 905 media + 888 relations).

scripts/migrate-to-supabase.ts

Frontend Configuration Center + Memory Cache

Unified environment variables, application-level caching, implementing "three-tier caching".

Three-tier cache hit rate breakdown:
- Layer 1 (Build SSG): 60% hit rate (first visit)
- Layer 2 (ISR Edge): 30% hit rate (re-visit within 1 hour)
- Layer 3 (Memory): 10% hit rate (category/settings API)
Total hit rate 90%, API calls 12 → 2.6 (-78%)

lib/configService.ts

Security & Privacy

  • Data minimization: No collection of names/phone numbers; Umami replaces third-party tracking; categorized cookie consent.
  • At-rest/in-transit encryption: Postgres/S3 encryption, TLS1.3, site-wide HSTS.
  • Retention & access control: Access logs 30 days; Public/Authenticated/Admin permission definitions.

Architecture Decision Records (ADR)

  • SPA → Next.js App Router (SSG/ISR): SEO & performance first.
  • Public content zero-auth + RLS: Cleaner frontend, security fallback in DB.
  • Supabase Storage (public bucket + CDN): Balance of cost/complexity/latency.

My Role

  • · Architecture Design: Defined Vue → Next.js migration path, SSG/ISR strategy
  • · Strapi Integration: Content Types design, i18n configuration, permission policies
  • · Performance Optimization: Three-tier caching, image optimization, Bundle analysis & optimization
  • · Data Migration: Wrote migration scripts, SQLite → Supabase (Postgres + Storage)
  • · GDPR Compliance: Umami integration, Cookie policies, data minimization
  • 💡 Complexity: Solo responsibility for frontend (68 files) + backend (42 files) + DevOps + data migration

Next Steps

  • Integrate Sitemap + RSS (improve crawl efficiency)
  • Add Edge caching and regional routing (EU/US nodes)
  • Implement semantic search for artwork filtering (vector search, technology TBD)

Tech Stack

Next.js 14 App Router / SSG+ISRStrapi v5 CMSSupabase EU (Postgres+Storage)GDPR-friendly (Umami)