Admin
A private CMS for Portfolio and Blog, with shared validation contracts, live capability checks, and Drizzle writes.
- Next.js Server Components
- Supabase SSR and RLS
- Typed CMS contracts
- RBAC and revalidation

The product tension
Editorial changes should not require editing application source, but hiding an editor route is not enough to protect a CMS. Admin needed to write the same records Portfolio reads while checking the caller at each privileged operation.
What I built
Admin provides section-owned editing workspaces for Portfolio and Blog. Forms and API handlers share typed content contracts with the public reader. Work visibility and case-study publication remain explicit controls; Blog records have their own publication state.
The editor validates a write, checks the live caller capability, and updates the canonical content through Drizzle. Auth owns interactive entry and account security; Admin owns whether that identity can perform an editorial or access-management operation.
How the system fits together
The request proxy handles admission and MFA step-up where required. API handlers still authorize their specific operation. Product data modules run validated CMS and IAM commands through the restricted Admin database boundary. Supabase platform administration remains server-side and capability-gated.
Shared packages contain identity, session mechanics, and content contracts. Admin and Portfolio do not import each other’s screens. See the CMS walkthrough for the editorial flow.
The calls that shaped the build
- 01
Share the record contract
The form, write API, and public reader agree on fields and publication rules instead of maintaining three interpretations of the same content.
- 02
Authorize the operation, not just the page
Admission to Admin does not grant every write. Live capabilities distinguish read-only access from content and access-management mutations.
- 03
Reduce latency across the request path
The applications now run in Vercel’s Mumbai region near the database. Repeated auth work and database reads were reduced alongside the Drizzle migration; no isolated ORM speedup is claimed.
Trust boundaries
The app is private. MFA step-up applies when a factor exists. Service-role and Vercel tokens never ship to the browser. Public Portfolio queries never receive unpublished rows or privileged credentials.
What I chose—and accepted
A separate editor adds deployment and session coordination. Explicit capabilities and shared contracts require maintenance, but make the difference between reading a draft and publishing it visible in the implementation.
Why the work matters
Content updates reach the same CMS records used by the public site without a code change for each edit. Read-only and editing access remain distinct, and database transport changes stay behind the existing APIs.
What I would improve next
Add preview and revision comparison before publishing, keeping publication an explicit choice rather than an autosave side effect.