Portfolio — A Site Where Publishing Is a Database Write
Content as rows instead of files, which makes publishing a save — and makes the write endpoint the most security-sensitive file in the repository.
The problem
The obvious architecture for a content site is markdown files and a deploy. It works until you want to fix a typo from a phone, reorder what leads the home page, or keep a draft only you can see — at which point every small edit means pushing to main and waiting on a build to check a sentence. Moving content into a database removes the waiting, but it also makes a single HTTP endpoint the trust boundary for the entire site, and that endpoint has to be the only path in.
The solution
Store every project, case study and post as a row in Cloudflare D1, rendered per request, with markdown converted to HTML on write so a page view is a read and nothing more. Funnel every write through one endpoint whose column identifiers all come from a source-defined allowlist, because a table or column name cannot be a bound parameter in SQL and a column that gets dropped silently is a save that reports success and loses work.
Outcomes
- The project frontmatter form carries a completeness checklist with real thresholds — summary length, tag and stack counts, highlight counts, a repository link — and a live preview built from the same classes the real card uses.
- AI edits are reviewed per field with individual Keep and Revert, and any manual edit to a marked field clears the mark.
- Database constraint failures are translated into sentences naming the field and what to do, instead of surfacing raw SQL error text.
+3 more in the write-up
Inside the write-up
- A portfolio is a blog with worse defaults
- The part that turned out to be the actual work
- Markdown in, HTML already made
- Rows are read back in the shape they are written
- The admin knows what a good project page looks like
- Where the secrets are not