When I added a blog to my site, I didn't want it to feel like a separate product. I wanted it to look, feel, and behave like the rest of the site while still keeping a simple writing workflow behind the scenes.
That idea shaped everything.
Instead of building a separate backend rendered blog, I split responsibilities:
- Frontend - handles UI, routing, search, and reading
- Backend - handles API, storage, auth, and publishing
This keeps things clean and maintainable.
Frontend: Owns the Blog Experience
The blog lives inside the main frontend app:
- /blog - list of posts
- /blog/:slug - individual post
This means:
- Same design system
- Same navigation and layout
- No "separate CMS feel"
The blog pages are simple:
- Fetch posts from the API
- Support search
- Render content consistently
Writer/admin features are not exposed here; this side is strictly for readers.
Backend: API Only
The backend does one job: serve data and handle writer actions.
It provides endpoints for:
- Posts (list + detail)
- Auth (login, logout, password)
- Post management (create, edit)
- Uploads (images)
- Markdown preview
No HTML rendering. Just JSON.
This avoids mixing UI with business logic.
Database: Simple by Design
I use MySQL with minimal structure.
blog_posts
- slug, title, summary
- markdown + HTML content
- cover image
- timestamps
blog_settings
- stores things like password hash
Since I'm the only writer, I skipped:
- roles
- drafts
- approvals
Less complexity, faster workflow.
Markdown as the Source
I write in Markdown, not HTML.
- Markdown = source of truth
- Backend converts to sanitized HTML
- Both versions are stored
There's also a preview endpoint, so what I see before publishing is exactly what gets published.
Writer Workflow: Fast and Private
The admin system is built for speed:
- Simple password login (session based)
- Create/edit posts
- Upload images
- Preview Markdown
Key choices:
- No public admin access
- CSRF protection included
- No drafts - publishing is immediate
This matches how I actually write.
Image Uploads
Images go through the backend:
- Validates type and size
- Stores in uploads directory
- Returns a public URL
Simple and controlled, no external asset juggling.
Static + Live Data
I combine static generation with live API updates:
At build time:
- Prerender /blog and posts - fast and SEO friendly
At runtime:
- Fetch latest data from API - stays fresh
Result:
- Fast pages
- Up to date content
- No need to rebuild after every post
Why This Architecture Works
Each layer does what it's best at:
- Frontend - UI & experience
- Backend - data & auth
- Build step - static output
- API - freshness
This avoids:
- heavy CMS complexity
- disconnected blog systems
- messy backend rendering
Final Thought
The tools (React, PHP, MySQL, Markdown) aren't the important part.
The key decision was architectural:
Keep the frontend for reading, the backend API only, and the workflow simple.
For a one writer blog, this setup is clean, fast, and easy to maintain.
