ProductionEdge architecture2026
This site
A portfolio, technical journal and project archive running entirely on Cloudflare Workers, D1 and R2.
Why it existsA DevOps engineer's own site is a claim about how they build things. It should hold up to inspection.
Overview
The site you are reading. Next.js rendered on Cloudflare Workers, all content in Cloudflare D1, all media in Cloudflare R2, with an admin interface for editing both.
It exists partly as a portfolio and partly as a constraint exercise. One compute platform, one database, one object store, no external services.
Problem
Most portfolios are either a static site with content in Markdown files, which means editing requires a commit and a deploy, or a static site plus a hosted CMS, which means a third-party dependency and a monthly bill for storing a few thousand words.
There is also a credibility problem specific to this field. An infrastructure engineer whose own site is a template on a shared host has undercut the argument before the reader reaches the projects page.
Approach
Two Workers with a hard boundary between them.
The frontend Worker renders Next.js and holds no database or storage bindings at all. The API Worker owns every D1 query and every R2 object. They communicate over a service binding, which is an in-process call inside Cloudflare's runtime, with no public network hop, no CORS and no DNS lookup.
The separation is not decoration. It means there is exactly one place where SQL is written, and the rendering layer cannot reach around it. If the frontend needs data it does not have, the answer is an API endpoint, not a query in a page component.
Architecture
Frontend Worker
Next.js App Router, server-rendered. Data-driven routes render per request against D1 rather than being baked at build time, so publishing an article is a database write, not a deployment. Client JavaScript is limited to the command palette, the theme toggle, forms and the lightbox.
API Worker
A hand-written router. No web framework: the Workers runtime already provides Request, Response and URLPattern, and a portfolio API does not need middleware composition. Public read endpoints, a contact endpoint, a search endpoint, and an authenticated admin surface for content and media.
D1
One database, twenty-four tables. Projects, articles, notes, lab entries, résumé records, taxonomy, media pointers, messages and settings. Repeating scalar lists are JSON columns; anything genuinely relational has a real table and a real foreign key.
R2
All binary content. Project screenshots, architecture diagrams, article images, the résumé PDF. Objects are served back through the API Worker, which sets long immutable cache headers, so R2 is read once per edge location per object.
Search
Scored queries across projects, articles, notes, experience and skills, in D1. No search service, no client-side index shipped to the browser.
Technology
Next.js and TypeScript on the frontend. Cloudflare Workers and TypeScript on the backend. Cloudflare D1 for data, Cloudflare R2 for objects. The OpenNext Cloudflare adapter builds the Next.js application into a Worker. Wrangler for local development, D1 migrations and deployment. No CSS framework, no component library, no state library, no backend framework, no ORM.
Implementation
The API Worker has zero runtime dependencies. Routing is URLPattern against a route table. Validation is a small set of typed guards. Responses go through one envelope helper so every endpoint returns the same shape and every error carries a code the frontend can branch on.
Admin authentication is PBKDF2-SHA256 over WebCrypto, with session tokens stored as SHA-256 digests in D1. The plaintext token exists only in an HttpOnly, Secure, SameSite=Strict cookie. A database dump yields no usable sessions.
The design system is handwritten CSS: custom properties for the palette and type scale, CSS Modules per component, one .glass material used deliberately on navigation, floating panels, metadata chips and overlays. Content surfaces are opaque paper, because long-form text on frosted glass is a legibility problem dressed as a style.
Reveal animations run through a single IntersectionObserver shared by every element on the page, and the whole motion system collapses to no-ops under prefers-reduced-motion.
Challenges
Rendering per request versus caching. Server-rendering every route against D1 keeps content editable without a deploy and puts a database round trip in the critical path. Resolved by keeping queries narrow and indexed, and by letting R2-served media carry the aggressive caching instead.
Service bindings in local development. A service binding requires the target Worker to exist. Locally that means two processes. The data layer prefers the binding and falls back to an HTTP call to a local wrangler dev, so a single command still produces a working site.
No ORM, by choice. Hand-written SQL means hand-written row mapping. The mitigation is that every table has exactly one mapper function, and the shared types package makes a schema change a compile error on both sides.
Restraint under a glassmorphism brief. The temptation is to make every surface translucent. Glass on a body-text column is unreadable, and glass on everything reads as a template. Keeping it to navigation, overlays and small metadata panels took more discipline than any technical decision here.
Decisions
Two Workers, one data owner. The frontend having no D1 binding is a structural guarantee, not a convention.
Hand-rolled router over a framework. The runtime already provides the primitives. A dependency here would be for ergonomics I do not need at this size.
Content in D1, not in Markdown files. Publishing should not require a deploy. That single requirement rules out the file-based approach and pulls in the whole admin surface.
Handwritten CSS. A utility framework would have been faster and would have made the restraint the brief asks for harder to hold.
Media through the Worker rather than a public bucket. One place to set cache headers, one place to add access rules later, and no publicly enumerable bucket.
Results
The site runs on the stack described, with content served from D1, media from R2, and an admin interface that edits both without a deployment. Every route has its own metadata, the sitemap is generated from the database, and the whole thing sits inside the constraint it set: Next.js, Workers, D1, R2, nothing else.
The most useful outcome is that adding a project or an article is now a form submission rather than a code change, which is the only reason a portfolio ever stays current.
Stack
Frontend Worker
- Next.js App Router
- Server-rendered per request
- No D1 or R2 bindings
- Handwritten CSS and CSS Modules
API Worker
- Zero runtime dependencies
- URLPattern router
- Typed validation guards
- Single response envelope
Data
- Cloudflare D1, 24 tables
- Foreign keys and covering indexes
- Scored SQL search
- Migrations under version control
Objects
- Cloudflare R2
- Served through the Worker
- Immutable cache headers
- Logical folder prefixes
Security
- PBKDF2-SHA256 over WebCrypto
- Session tokens hashed at rest
- HttpOnly, Secure, SameSite cookies
- Honeypot and rate-limited contact form
Measurements
- Runtime dependencies (API)
- 0Workers runtime primitives only
- Data stores
- D1 and R2No third-party service of any kind
- Publish path
- Database writeNo deployment required


