0-Tech Schema Generator — Next.js + Supabase Edition by Bree Sharp0-Tech Schema Generator — Next.js + Supabase Edition by Bree Sharp

0-Tech Schema Generator — Next.js + Supabase Edition

Bree Sharp

Bree Sharp

Ported Next.JS + Vercel + Supabase
Ported Next.JS + Vercel + Supabase

Why I ported it

I already ship a Cloudflare Workers version of this tool and use it as the productized $9 entry point of my SEO services. The product logic — page fetch, JSON-LD extraction, schema validation, page-type detection, AI-generated findings — was solid. The stack was the only thing I wanted to expand: Next.js, Vercel, and Supabase are the patterns most of the AI-application Contra jobs target right now, and I wanted a deployed, paid, real-user reference on those three.
So I ported it. Focused build. End state: same product, two deployments, two visual identities.

What stayed the same

Anthropic system prompt (~6KB, structured-output JSON-LD generation, calibrated for non-technical small-business-owner audience)
The deterministic analysis pipeline: page-type detection (homepage / about / contact / article / product / service / FAQ), business-signal heuristics (LocalBusiness from phone-in-content, address-in-JSON-LD), per-type required/recommended property rules with subtype satisfaction (Plumber satisfies LocalBusiness, NewsArticle satisfies Article)
The CMS-specific install instruction templates for WordPress, Webflow, Squarespace, Wix, Shopify, static HTML
Pricing: $9 for 100 audits, 3/day free anonymous, 8/day free authenticated

What changed — the actual port work

Compute model: Worker handler → Next.js Route Handler. Same Web Request / Response shape, near-identical code. Worker env bindings became process.env vars. The bulk of index.js routing reduced to app/api/audit/route.ts.
HTML parsing: Cloudflare HTMLRewriter → node-html-parser. HTMLRewriter is streaming + selector-based; node-html-parser is buffered + DOM-tree. The interface ended up cleaner — single parse(html) call, root.querySelectorAll(...). Same output shape.
Page rendering: Browser Rendering (Puppeteer) → plain fetch. This is the most interesting trade-off. The CF Worker uses Cloudflare's Browser Rendering binding to spin up a headless Chromium per request and capture client-rendered HTML. Vercel Hobby tier can't do that cheaply — too much memory, too much execution time. The Next.js port uses plain fetch() and gets server-rendered HTML only. For most schema-audit targets this is fine (CMS plugins and SSR frameworks emit JSON-LD server-side). For pure-SPA targets it misses anything injected after hydration. Documented limitation; case-study material in itself.
State: Cloudflare KV → Supabase Postgres. KV is key-value, eventually consistent, schemaless. Supabase is Postgres with Row Level Security. I gained: typed schema, query flexibility (recent-audits list per user), foreign keys, atomic updates. I lost: edge-distributed reads. For this workload — a handful of writes per audit — Postgres is the right call.
Auth: KV-stored email + IP-hash tier → Supabase Auth magic links. The Worker has its own minimal auth (email = identity, KV stores email → credit balance). Supabase Auth replaces all of it with passwordless magic links, proper session cookies, @supabase/ssr for App Router cookie handling, and a Next 16 proxy.ts (formerly middleware.ts) that refreshes expired tokens on every request. Cleaner UX, less code I have to maintain.
Payments: Stripe Payment Links → Stripe Checkout Sessions API. The Worker uses a hosted Payment Link (no API call required, you just link to it). The Next.js port creates Checkout Sessions via API, which means I can prefill customer_email, attach client_reference_id: user.id, set per-session metadata, and stamp Stripe events with my user ID. Webhook handler runs in a Node.js Route Handler using req.text() to preserve the raw body for signature verification — same security profile as the Worker version, but using the official stripe Node SDK throughout.
Frontend: HTML strings concatenated in a Worker → Server + Client Components. The CF version emits a single HTML string from a Worker. The Next.js version uses an async Server Component for the marketing page (server-side fetch of the signed-in user via Supabase) and a Client Component ('use client') for the audit form and results. The split was the only piece of App Router that took real adjusting — once you internalize that hooks and event handlers force a 'use client' boundary, the model clicks.

Anthropic specifics

Both versions call Claude Sonnet 4.6 with adaptive thinking + low effort + an ephemeral cache_control breakpoint on the system prompt. The structured analysis is passed as the user message; the model returns JSON matching a fixed shape. The Next.js port uses @anthropic-ai/sdk directly (vs the Worker's raw fetch to the API). Same prompt, same model, same output shape.

Visual treatment — deliberate brand split

The two deployments share zero visual identity, on purpose.
The Cloudflare version is consultant-clean — white background, sans-serif, dashboard screenshots, minimal flourishes. It lives at bree-sharp.com/tools/schema-generator/ and matches the rest of my services site.
The Next.js version is design-forward editorial. Warm cream background (#FAF7F2), Fraunces variable serif for display type with its softness axis dialed up on accent words, Inter for body, JetBrains Mono for navigation labels and code, single terracotta accent (#C44536). Numbered findings, pull-quote summaries, italic CMS tab labels, mono section markers (§ 01, § 02), and a soft cream shimmer that sweeps diagonally across the dark Analyzing button during requests. Reads more like a small-print magazine than a typical SaaS tool.
Next.js editorial version screenshot
Next.js editorial version screenshot
Same product, different audience: the consultant version sells to small business owners who already trust my name; the editorial version sells the stack and the taste to AI-application buyers browsing Contra portfolios.

What surprised me

Next 16 just renamed middleware.ts to proxy.ts while I was building this. The migration warning appeared in dev mode. One rename + one function-name change and it was done. Worth pinning Next versions for production stability.
The Supabase Auth flow is much shorter than I expected. Magic link, callback route, server client, browser client, proxy for token refresh — that's the whole surface, no JWT plumbing of my own.
Stripe Checkout Sessions vs Payment Links is a meaningful UX difference. The Worker collects email after payment (because Payment Links don't know who the buyer is until checkout completes). The Next.js port attaches the user ID upfront via client_reference_id, so credits land in the right account automatically. Better flow for authenticated buyers.

Stack summary

Cloudflare version → Next.js version, layer by layer:
Compute: Cloudflare Workers → Vercel serverless
HTML rendering: Puppeteer (Browser Rendering) → plain fetch (no JS execution)
HTML parsing: HTMLRewriter → node-html-parser
AI client: Raw fetch to Anthropic → @anthropic-ai/sdk
State: Cloudflare KV → Supabase Postgres + Row Level Security
Auth: KV-stored email + IP hash → Supabase Auth (magic link)
Payments: Stripe Payment Link → Stripe Checkout Sessions + webhook
Frontend: HTML string from Worker → Server + Client Components
Styling: Inline + global CSS → Tailwind v4
Design language: Consultant-clean → Editorial (Fraunces + cream)

Links

Next.js + Vercel deployment: https://schema-generator-nextjs.vercel.app
Cloudflare Workers deployment: https://bree-sharp.com/tools/schema-generator/
Like this project

Posted May 12, 2026

Ported my Cloudflare Workers schema audit tool to Next.js App Router + Supabase + Stripe + Vercel. Same product, two stacks, deliberately distinct design.