Rebuilding My Static Blog with Build-Time Data and Instant Search

Static sites are supposed to be fast, simple, and reliable. But over time, my personal blog started behaving like a dynamic app - runtime API calls, pagination logic everywhere, and fragmented view counts spread across platforms.
Last week, I rebuilt the blog section of ravgeet.in (Nuxt.js) to fix this properly. The end result is still a static site, but now it feels alive: aggregated view counts, instant search and sorting, and zero runtime dependencies on external APIs.
This post walks through the thinking, architecture, and trade-offs behind that rebuild.
The problem with my old setup
Originally, my blog worked like this:
Blog content lived on Hashnode (canonical source)
Some posts were also cross-posted to Dev.to
Pages fetched blog data at runtime using Hashnode’s GraphQL API
Pagination logic (
hasNextPage, cursors) lived inside the UI
This had a few downsides:
A static site depending on live APIs felt wrong
Local development and builds were slower and flaky
Adding features like search or sorting would require more APIs
I wanted the blog to stay static - but smarter.
Build-time data as a contract
The core decision was simple:
Move all external data fetching to build time, and treat the result as immutable static data.
Instead of fetching blogs at runtime, I introduced a build step that:
Fetches blogs from Hashnode
Fetches articles from Dev.to
Matches the same article across platforms
Aggregates view counts
Writes everything into a single JSON file
At runtime, the site only reads from that JSON.
Hashnode + Dev.to
↓
Build-time fetch & normalize
↓
static/blogs.json
↓
Nuxt UI (search, sort, views)
This one decision simplified everything else.
Fetching and aggregating blog data
Hashnode: canonical content
Hashnode remains the source of truth for:
Title, slug, content, tags
Publish date
Cover image
Base view count
I fetch all posts using Hashnode’s GraphQL API with pagination handled inside a Node.js script.
Dev.to: distribution and extra reach
Dev.to is where additional readers come from, so ignoring those views felt wrong.
Using the Dev.to API (with a personal access token), I fetch all my articles and extract:
slugcanonical_urlpage_views_count
Matching articles across platforms
This is the tricky part. Articles are matched using a layered strategy:
Slug match
Canonical URL match
Title match
Once matched, the final view count becomes:
combinedViews = hashnodeViews + devtoViews
The output for each blog includes:
Combined views
Platform-specific views (for debugging)
Dev.to URL (if matched)
Writing the static data contract
All processed data is written to the static/blogs.json file.
This file is:
Generated at build time
Git-ignored
Treated as read-only by the app
It also includes metadata like the last updated time and the total blog count.
This JSON file effectively replaces my entire blog API.
Replacing runtime APIs with static services
Previously, services/blogs.js made live GraphQL calls. After the refactor:
The service dynamically imports
blogs.jsonfind,findOne, andsearchall operate locallyNo Axios
No pagination state
No network failures
From the UI’s perspective, nothing changed - but under the hood, everything became predictable.
Instant search and sorting
Once all blog data is local, search becomes trivial.
I added:
Client-side text search (title, brief, tags)
Sorting by:
Published date (recent / oldest)
View count (most / least)
Because the dataset is small and static:
Search results are instant
No debouncing hacks
No loading states
Sorting is deterministic
This dramatically improves discoverability without introducing a search service.
Trade-offs and lessons learned
This approach isn’t perfect:
Build time increases slightly
The JSON file grows over time
It’s not suitable for real-time analytics
But for a personal blog, the trade-offs are worth it.
The key takeaways from the refactor that made me realize that:
Static doesn’t mean lifeless
Build-time data pipelines are underrated
One clean data contract simplifies UI, UX, and performance
If you’re curious, the full implementation lives in the ravgeet.in repository.
I share tips on how to get started with freelancing, remote jobs, developer-related stuff, startup ecosystem, and lots of insider secrets with my subscribers.