zaki-ns Platform
Full-Stack Developer32 weeksLiveTeam of 1

zaki-ns Platform

PostgreSQL-backed portfolio CMS with admin dashboard, bilingual public site, and schema-driven content

NJNext.js 16R1React 19TypeScriptTypeScriptPrismaPrismaPostgreSQLPostgreSQLNnext-intlTailwind CSSTailwind CSSCCloudinary
View LiveSource Code

Project Intelligence

Duration

32 weeks

Technologies

8

Status

Production

Key Challenge

Designing a schema and seed pipeline that scales from static lib/data.ts to PostgreSQL without breaking public pages during migration

Skills Demonstrated

Full StackSystem DesignDatabase DesignCMSi18nDevOps

TL;DR

Built a deployable zaki-ns platform where public pages read from PostgreSQL via Prisma, admin forms persist through Server Actions, and revalidateTag keeps the cached site in sync—covering projects, labs, articles, products, journey, and site settings.

Problem

Mock data in lib/data.ts drifted from admin edits and could not scale to multiple deployable portfolios

Solution

Prisma schema with translation rows, JSON seed pipeline, and cached DB getters with admin Server Actions

Result

One codebase deployable per customer with admin-editable content and no localStorage drift

50+

Prisma models

6

DB-backed content types

Key Outcomes

10+

Content types

50+

Prisma models

12

Admin routes

Curated Visuals

Screenshot 1
1 / 25

Results & Impact

Production-grade portfolio CMS that demonstrates full-stack engineering on the site itself.

The platform showcases its own architecture—every project, lab, and article is editable through the admin the visitor can explore.

10+

Editable content areas

0

localStorage content drift

Architecture

Architecture diagram

Public pages are Server Components that call locale-aware getters in lib/*.ts. Each getter uses withDbFallback to read from PostgreSQL via *-db.ts mappers, falling back to lib/data.ts during migration.

Admin client forms call Server Actions that upsert Prisma records—including nested translations, sections, and relations—then revalidate cache tags so the public site reflects edits without redeploying.

Content is modeled with translation tables (project_translation, lab_translation, etc.) and shared content_section rows for long-form lab and article bodies.

Infrastructure & Deployment

Docker Compose for local PostgreSQL, deployable to Vercel with Neon/Supabase. Cloudinary for media. ADMIN_SECRET gates /admin via HMAC session cookie.

Features

Core

Admin CRUD

Full create/read/update/delete for projects, labs, articles, products, experience, skills, and journey.

Core

Cached DB reads

Public getters with unstable_cache and revalidateTag for fast SSR without stale content.

Core

Bilingual routing

next-intl locale segments with translation-ready schema.

Secondary

Rich case studies

Slides, challenges, architecture, metrics, and screenshots as first-class relations.

Secondary

Contact inbox

Form submissions stored in DB with optional email notification.

Planned

Quote calculator

Interactive pricing page with project types, complexity tiers, and add-ons.

Challenges & Solutions

1

Nested admin saves vs relational schema

The Problem

Project case studies include slides, challenges, screenshots, and translations—naive form posts dropped nested arrays on partial saves.

How I Solved It

Server Actions merge nested payloads server-side with explicit delete-and-recreate for child collections inside Prisma transactions.

await prisma.$transaction(async (tx) => {
  await tx.projectSlide.deleteMany({ where: { projectId } })
  await tx.project.update({
    where: { id: projectId },
    data: { slides: { create: slides } },
  })
})
revalidateTag('projects')
2

Migration without downtime on public pages

The Problem

Replacing lib/data.ts imports broke pages before DB seed was ready.

How I Solved It

withDbFallback tries Prisma first and falls back to static seed data until counts verify.

3

Locale content strategy

The Problem

Full bilingual body copy for every entity was not ready, but UI chrome needed FR immediately.

How I Solved It

Option A: English body from DB, French UI strings from lib/translations.ts; schema supports Option B translation rows when ready.

Lessons Learned

  1. 1

    Seed from version-controlled JSON

    Generating structured content JSON and importing it through lib/data.ts keeps demo portfolios reproducible and reviewable in PRs.

  2. 2

    Translation tables early

    Adding locale columns on translation rows from day one avoids painful schema migrations when bilingual CMS editing arrives.

  3. 3

    Cache tags per aggregate

    Granular revalidateTag keys (projects, labs, site) prevent stale public pages without flushing the entire Next.js cache.

  4. 4

    Domain types before Prisma imports

    UI and admin import Project/Lab types from lib/, never Prisma types—mappers isolate the ORM from React components.

What I'd Do Differently

Wire quote calculator settings to the database earlier instead of keeping lib/quote-data.ts hardcoded alongside otherwise DB-backed site content.