realworld.io with NestJS + HTMX
Another HTMX and realworld.io project. Previously I have built Laravel + HTMX and Go Fiber + HTMX. This time I am using NestJS with TypeScript and Handlebars templates.
I already had experience with NestJS from building the fourth version of Fa'Diel. Pairing it with HTMX and the RealWorld spec felt like a natural next step to combine structured backend patterns with lightweight frontend interactivity.
The project: NestJS + HTMX and realworld.io
This was created to demonstrate a fully fledged fullstack application built with:
- NestJS - progressive Node.js framework with modules, decorators, dependency injection
- TypeORM - ORM with better-sqlite3 driver
- Handlebars (hbs) - server-side template engine
- HTMX - AJAX, CSS transitions, and partial swaps directly in HTML
- express-session - session-based authentication
- that adheres to the RealWorld specification
Dual rendering architecture
The app uses two layers of controllers working on the same URL paths:
- SSR controllers render full HTML pages on initial load via
@Render()decorator - HTMX controllers (under
/htmx/*) return partial HTML fragments for subsequent SPA-like navigations
All links use hx-get + hx-target="#app-body" + hx-push-url, so clicking around feels like a single page application without a build step. The navbar and page title update via OOB (out-of-band) swaps in the same HTMX response.
HTMX integration highlights
- Favorite and follow buttons self-replace on click with
hx-target="this"+hx-swap="outerHTML" - Feed tabs switch content by targeting
#feed-post-previewwithout reloading the page - Form validation errors retarget to specific DOM elements using
HX-Retarget+HX-Reswapheaders - Unauthenticated actions trigger
HX-Redirectheader to send users to the sign-in page - Content feeds lazy-load on page load via
hx-trigger="load"
Advantages:
- NestJS module system keeps the codebase organized even as it grows
- Handlebars partials enable reusable UI components without a frontend framework
- HTMX handles the SPA feel while the server stays in control of routing and state
- SQLite via better-sqlite3 means zero database setup, just clone and run
- Dual controller pattern clearly separates full-page renders from HTMX fragment responses
The repository and live example can be accessed below:
RepositoryLive Example