Skip to content

Portfolio

A Next.js portfolio with a shared PostgreSQL CMS, Drizzle content queries, technical case studies, and public aggregate analytics.

By End-to-end case study
  • Next.js Server Components
  • Supabase and PostgreSQL CMS
  • Google Drive PDF export
  • Resend contact workflow
Founders, recruiters, and engineers get one evidence-led path from positioning to shipped systems, technical depth, current experience, and contact.
01 / Problem

The product tension

I needed to update experience, project evidence, and articles without maintaining the same facts in both CMS records and source files. The public site also needed to distinguish published work from drafts without depending on a visitor being signed in.

02 / Solution

What I built

Portfolio reads published CMS content through typed Server Components and a one-minute cache. Work visibility and case-study publication are separate controls; hidden or unpublished studies do not become public detail pages.

The name comes from a shared identity registry, while positioning, biography, and project narratives stay editable in the CMS. Résumé delivery, contact email, GitHub activity, and aggregate Cloudflare analytics have their own server-side provider boundaries. See the CMS walkthrough and the Drizzle migration.

03 / Architecture

How the system fits together

A product-owned loader composes public content reads into one SQL statement through Drizzle. It selects explicit columns from the portfolio schema under anonymous RLS, then maps the results into editorial view models.

Admin writes the same content contract through authorized operations. Portfolio owns its editorial components; identity, URL, SEO, and content contracts are shared packages. This keeps the public reading experience separate from the authenticated application shell.

04 / Decisions

The calls that shaped the build

  1. 01

    Keep one source for editable content

    Missing required CMS records surface as errors instead of silently falling back to a second static biography or project catalog.

  2. 02

    Use a restricted public reader

    Portfolio uses its own database login and anonymous RLS for public content, including requests from signed-in administrators. It has no service-role client.

  3. 03

    Make discovery part of publishing

    Published pages supply canonical URLs, sitemap entries, and person or article structured data. Blog and case-study bylines link back to About.

05 / Security

Trust boundaries

Public selects are column-limited. Administrative writes never run in this app. Contact submissions are validated and throttled; I do not persist raw network identifiers as a lead database. Analytics on the public site are aggregate Cloudflare signals, not visitor dossiers.

06 / Tradeoffs

What I chose—and accepted

CMS reads introduce database and cache dependencies. Incremental regeneration can briefly serve older content after a save; I accept that freshness window to avoid fetching every editorial record on every visit. Provider failures have their own fallback or unavailable states rather than inventing replacement content.

07 / Outcome

Why the work matters

Visitors can move from a project summary to its implementation, tradeoffs, and related writing. Admin and Portfolio use the same records, and public content queries stay constrained by RLS. The deployed pages can be checked through the live site, with implementation details in the linked articles.

08 / Next

What I would improve next

Add a preview and field-level comparison before publication, then measure the reading experience across devices using the existing aggregate performance signals.