Skip to content
Suleiman Francis

SeamHealth Blog · SeamHealth Group

Shipping a `CMS` driven blog with production hardening from the first commit.

Next.js 16 and Sanity blog for SeamHealth: editorial content, a newsletter with deliverability checks, rate limiting, CSP and CI.

Role
Lead engineer
Timeline
Jul 2026
Team
Small team; I wrote the initial implementation and most of the commits.
Platform
Web · content site
Stack
  • Next.js 16
  • TypeScript
  • Tailwind CSS
  • Sanity
  • GROQ
  • React Hook Form
  • Zod
  • TanStack Query
  • GitHub Actions

A SeamHealth Group site. The architecture is simplified.

Context

SeamHealth needed a blog for healthcare insights that editors could run without engineers: posts managed in a CMS, a newsletter, and good search and social previews.

My responsibility

I owned

  • The initial implementation: Next.js app, Sanity schemas, GROQ queries and the blog feature
  • The newsletter: subscription API with checks that an email address is real and deliverable
  • Production readiness: rate limiting, a Content-Security-Policy and structured logging
  • CI: a dedicated Sanity development project for builds, and retries for transient registry failures in the dependency audit
  • Security upgrades, such as moving Next.js to a version that fixed four high-severity CVEs

I collaborated on

  • Navigation, footer and design work with a second engineer

The challenge

A marketing blog is easy to build and easy to leave insecure: a public subscribe endpoint invites abuse, CMS content is untrusted input, and CI that depends on production content is fragile.

Constraints

Editors, not engineers
Content has to be managed entirely in the CMS.
Public endpoints
The newsletter API is reachable by anyone.
CI isolation
Builds shouldn’t depend on, or touch, production content.

Architecture

  1. Routes
    • Blog listing
    • Post pages
    • Embedded Sanity Studio
    • OG image route
  2. Features
    • blog
    • newsletter
  3. Data access
    • GROQ queries → repositories → services
  4. API
    • Newsletter endpoint: validation, deliverability check, rate limit
  5. CMS
    • Sanity
Simplified.
  • Pages never talk to the CMS directly: GROQ queries sit behind repositories, and business rules live in services.
  • Skeleton loaders match each page’s structure, so loading doesn’t shift the layout.

Key decisions

01Verify email addresses before subscribing them

Problem
A public subscribe form collects typos, disposable addresses and junk that harm sender reputation.
Options
  1. Accept anything that passes a regex
  2. Check that the address is real and deliverable first
Trade-offs
Verification adds a step and a dependency; skipping it degrades the list and email deliverability over time.
Decision
Validate with Zod, then check deliverability before adding the subscriber.
Reason
A smaller, real list is worth more than a large one that bounces.
Result
Only deliverable addresses reach the newsletter list.

02Harden before launch, not after

Problem
The newsletter endpoint and CMS-rendered pages were exposed to the internet from day one.
Options
  1. Launch, then harden
  2. Add rate limiting, CSP and logging before launch
Trade-offs
Hardening first delays launch slightly; hardening later means running unprotected in public.
Decision
Rate limiting on the API, a Content-Security-Policy, and structured logging before production.
Reason
The cheapest time to add security controls is before anyone depends on the system’s current behaviour.
Result
Abuse protection and searchable logs were in place before production.

Implementation

Content
Sanity schemas, Portable Text rendering and an embedded Studio at /studio.
SEO
Dynamic sitemap, robots.txt and an edge-rendered social preview image per post.
Tests
Six test files, all written by me.

Production

CI
GitHub Actions against a dedicated Sanity development project, with a dependency audit that retries transient registry failures.
Patching
Next.js upgraded to fix four high-severity CVEs.

Outcome

Built from the first commit to a hardened, `CMS` driven site that editors run without engineers.

Commits authored
19 of 32Measured
Including the initial implementation, from git history.
Readership
—Not measured
Impact metric unavailable.

Lessons

Small public sites need the same basics as big ones rate limits, CSP, logs and patched dependencies. They’re cheapest when added at the start.

Next iteration

  • Double opt-in confirmation for newsletter sign-ups.
  • Preview deployments wired to Sanity drafts so editors can see posts before publishing.