A single route handler, one data fetch, three response formats. This is the recommended server architecture for html★ backends.

The Problem

html★ fetches HTML fragments from the server and swaps them into the page. But the same endpoints often need to serve other consumers: full HTML pages for initial loads, JSON for mobile apps or APIs. Maintaining parallel endpoint sets is duplication.

The Three-Tier Response Model

A single route handler returns data. The response format is determined by who's asking:

Request TypeHow to DetectResponse
Initial page loadNormal browser requestFull HTML page
html★ swapX-Requested-With: htmlstar headerHTML fragment
API consumerAccept: application/jsonJSON data

One handler. One data fetch. Three formats. No duplication.

Express Example

Hono Example

Ruby on Rails Example

Django Example

The Vary Header

When the same URL returns different content based on request headers, you must set Vary: X-Requested-With. This tells caches (CDNs, proxies, browsers) that the response varies based on that header. Without it, a cached fragment might be served to a full-page request or vice versa.

Why Not Accept Headers Alone?

html★ sends X-Requested-With: htmlstar rather than relying on the Accept header because:

  1. Explicit intentX-Requested-With unambiguously says "this is an html★ request"
  2. Browser defaults — Browsers send Accept: text/html for normal navigation, which overlaps with fragment requests
  3. Cache differentiationVary: X-Requested-With cleanly separates full-page and fragment caching

The Accept header is still useful for distinguishing HTML from JSON responses.

Pattern A vs Pattern B

You don't always need server-side fragment detection. html★'s data-select attribute can extract fragments from full-page responses client-side:

Pattern A (full pages with data-select) is simpler and handles progressive enhancement automatically. Pattern B (server-side fragments) reduces bandwidth but requires server logic. Start with A, upgrade to B when you need it.

This Site Uses Pattern A

This documentation site uses data-select="#content" to extract the article content from full-page responses. Each page is a complete HTML document, and html★ picks out just the content area during SPA navigation — no server-side fragment logic needed.