Skip to content
AM
All projects
Full-Stack Web Application · CMS Featured Private

Building Master Construction & Developers — Full-Stack CMS

A marketing website and custom admin CMS for a construction and architecture firm: projects, services, blog, gallery, consultation bookings and inquiries, with secure admin sign-in and media on Cloudflare R2, built as one Next.js app on Neon PostgreSQL.

  • Next.js
  • React
  • TypeScript
  • PostgreSQL
  • Neon
  • Tailwind CSS
  • JWT
  • Cloudflare R2
  • Recharts

Private project, demo on request

This is client or employer work, so the source code is confidential and the live app sits behind a login. I can't share screenshots or credentials publicly, but I'm happy to walk you through it on a call: a live demo, the architecture and selected code.

Request a demo

Architecture

Overview

A marketing website and content management system for a construction and architecture firm, built as a single Next.js 15 App Router application. Visitors browse projects, services, a blog and a gallery, book consultations and send inquiries. The firm's staff manage all of that content, plus incoming leads, from a password-protected /admin dashboard. React client pages talk to Next.js Route Handlers that act as the backend. Content lives in Neon serverless PostgreSQL, and uploaded media goes to a Cloudflare R2 bucket.

System

text
 Public site (React 19)          Admin CMS (/admin)
        \                          /
         \  DataContext + api.ts  /     middleware: no cookie -> /admin/login
          v                       v
     Next.js Route Handlers  (/api/*)
     projects | services | posts | gallery | web-info
     bookings | inquiries | auth | upload | health
        |            |                    |
   getAuthenticatedUser  pg Pool        S3 client
   (JWT in httpOnly       |                |
    cookie / Bearer)      v                v
                   Neon PostgreSQL    Cloudflare R2
                   (site_* tables)    <- /api/upload/media/[...key]

Key design decisions

One deployable instead of two. The project started as a separate Express backend with its own frontend. I moved the backend into Next.js Route Handlers, so the API, the admin panel and the public site now share types, auth helpers and a single deploy.

Cookie-based sessions with checks in two places. Login compares bcrypt hashes and returns a signed JWT in an httpOnly, SameSite=Lax cookie, so client JavaScript never handles the token. Middleware sends anyone without a cookie away from /admin, and every write route verifies the token again on the server.

Media proxied through the app. Uploads go to R2 under foldered, timestamped keys. Pages reference /api/upload/media/... rather than bucket URLs, and the proxy serves files with immutable cache headers, so storage can move without rewriting stored content.

Singleton settings as JSON documents. Company info, hero and about settings are each stored as one JSON row and saved with INSERT ... ON CONFLICT DO UPDATE. Editors can add or change fields without a schema migration, while projects, posts, bookings and inquiries use normal columns.

The site keeps working if the API fails. A shared DataContext fetches every collection in parallel with Promise.allSettled and caches public content in localStorage. It falls back to bundled default content when a request fails, so one failed endpoint doesn't leave a page empty.

Stack

  • Frontend: Next.js 15 (App Router, Turbopack), React 19, TypeScript, Tailwind CSS, Radix/shadcn UI, Framer Motion
  • Backend: Next.js Route Handlers, jsonwebtoken, bcryptjs
  • Data: Neon PostgreSQL via pg connection pool
  • Media: Cloudflare R2 through the AWS SDK S3 client
  • Admin charts: Recharts

Key Implementation Highlights

  1. Moved a standalone Express API into Next.js Route Handlers, putting the public site, admin CMS and API in one TypeScript codebase.

    Architecture
  2. Built a CMS for projects, services, blog posts, gallery, site-wide settings, consultation bookings and contact inquiries.

    Admin
  3. Built admin auth with bcrypt password checks, JWTs in httpOnly cookies, route middleware and token checks on every write endpoint.

    Security
  4. Built drag-and-drop image uploads to Cloudflare R2, served back through a caching proxy route instead of public bucket URLs.

    Media
  5. Designed PostgreSQL tables for content and leads, keeping singleton site settings as JSON documents saved with upserts.

    Data
  6. Added booking and inquiry inboxes with status tracking, filtering and CSV export of bookings.

    Admin
  7. Cached public content in localStorage and loaded collections in parallel, so repeat visits render before the network responds.

    Performance
  8. Shared one bounded pg connection pool through a global cache so development hot reloads stopped opening new Neon connections.

    Backend

Interested in the technical details?

Let's Talk