8月11日、Andros.devが「HTML over WebSockets: real-time SPAs with barely any JavaScript」と題した記事を公開した。WebSocketでHTMLを直接送り込むアーキテクチャによって、JavaScriptをほぼ使わずにリアルタイムSPAを構築できるという手法について詳しく解説されている。
ReactやVueを中心とした「JSON API + JavaScriptフレームワーク」の構成が主流になって久しいが、その複雑さへの反動として「サーバーがHTMLを組み立てて直接送る」というアーキテクチャが静かに再注目されている。かつてのサーバーサイドレンダリングの焼き直しに聞こえるかもしれないが、WebSocketと組み合わせることで、クライアントリクエストを待たずにサーバーから能動的にUIを更新できる点が従来とは根本的に異なる。
JSONを捨てて、HTMLをそのまま送る
通常のSPA開発では、サーバーはJSONを返し、フロントエンドのJavaScriptフレームワーク(React、Vue、Angularなど)がそれを受け取ってHTMLを組み立てる。バックエンドとフロントエンドで2つのコードベースが存在し、APIという「契約」によって両者をつなぐ構造だ。
この記事が提案するのはその逆転発想である。サーバー側でHTMLを組み立て、そのまま送りつける。クライアントはJSONを解釈してHTMLを構築する必要がなく、受け取ったHTMLを所定の場所に差し込むだけでよい。このパターンは「**ハイパーメディア(Hypermedia)」あるいは「HTML over the wire**」と呼ばれ、2010年代後半からHTMLをAPIの応答形式として見直す動きとともに議論が活発化してきた。
そこにWebSocketを組み合わせると、さらに強力になる。WebSocketは双方向の永続的な通信チャネルであるため、クライアントがリクエストを送らなくてもサーバー側から能動的に更新を配信できる。これが「HTML over WebSockets」の核心だ。
従来のHTTPベースのフローと比較するとその差は明確だ。
従来(JSON over HTTP)
- ブラウザがHTTPリクエストを送信
- サーバーがDBを照会してJSONを構築
- ブラウザがJSONをパースしてHTMLを生成
HTML over WebSockets
- ブラウザが「
/article/2/が欲しい」とWebSocket経由で送信 - サーバーがDBを照会してHTMLをレンダリング
- 組み立て済みのHTMLがそのまま返ってくる
- ブラウザは受け取ったHTMLを所定の場所に配置するだけ
ステップ数が減るだけでなく、WebSocketは一度接続を確立すれば以降のやり取りでTCPハンドシェイクやHTTPヘッダーのオーバーヘッドが発生しない。「WebSocketプロトコルが魔法のように速い」わけではなく(HTTP/2やHTTP/3がその差を大きく縮めた)、組み立て済みHTMLを送ることでラウンドトリップを削減できる点が本質的な優位性だ。なお実際の実装では完全なHTMLではなく差分パッチのみを送るフレームワークも多く、転送量の最適化も考慮されている。
この発想の起源はElixir/Phoenix LiveView
このアプローチを最初に広めたのは、ElixirのWebフレームワークPhoenixの作者であるChris McCordだ。2019年のElixirConfでPhoenix LiveViewを発表し、わずか15分でReactもVueも使わずにリアルタイムのTwitterクローンを構築してみせた。そのデモは大きな反響を呼び、2024年12月にはLiveView 1.0がリリースされた。
このデモが呼び水となり、他言語でも同様の実装が続々と登場した。
主なフレームワーク
現在、主要な言語でHTML over WebSocketsの実装が存在する。
| 言語 | フレームワーク | トランスポート | サーバープッシュ |
|---|---|---|---|
| Elixir | Phoenix LiveView | WebSocket | あり |
| Ruby / Rails | Hotwire(Turbo + Stimulus) | HTTP + WebSocket/SSE | あり |
| Python / Django | Django Channels(Django Reactor) | WebSocket | あり |
| C# / .NET | Blazor(Interactive Server) | WebSocket(SignalR) | あり |
| PHP / Laravel | Livewire 3 + Reverb | WebSocket | あり |
| 言語非依存 | htmx | HTTP + WS/SSE拡張 | あり(拡張) |
| 言語非依存 | Datastar | SSE(※) | あり |
※DatastarはWebSocketではなくServer-Sent Events(SSE)をメイントランスポートとして採用している。双方向通信が不要なユースケースではSSEの方がインフラが単純でスケールしやすいという設計判断による。
HotwireはRuby on Railsコミュニティで急速に普及し、Rails 7以降はデフォルトのフロントエンド戦略として採用されている。htmxはサーバー言語を問わず導入できる軽量ライブラリとして、バックエンドエンジニアを中心に人気を集めている。
メリットとデメリットを整理する
メリット:
- レンダリングエンジンが1つになり、複雑さが減る
- APIが不要。サーバーがHTMLを生成して直接送る
- 状態はサーバーが保持。接続中のクライアントごとにプロセスが存在し、状態を記憶する(htmxのようなステートレス設計とは対照的)
- ブロードキャストが自然に使える。チャット、ダッシュボード、マルチプレイヤーゲームなどが容易に実現できる
- XSS耐性が高い。サーバー側でHTMLをエスケープして送るため、
<script>タグを送り込もうとしても無害なテキストとして届く - ReactやVueなどの重いフレームワークなしにSPAが構築できる
デメリット:
- サーバーリソースが増える。クライアントごとにWebSocketを維持し、状態をメモリに保持する。水平スケールには各フレームワークに応じた追加構成(例:DjangoであればChannels + ASGIサーバー + Redis)が必要になる。ただし記事の著者自身は、Raspberry Pi 3相当のハードウェアで600人の同時接続を問題なく処理したと述べている
- 物理的なレイテンシが大きい環境では「即時」の感覚が損なわれる
- オフライン非対応。接続が切れるとサイト自体が動かなくなる。再接続のUX設計が必要だ
- 学習コストがある。WebSocketサーバーの運用はHTTPより複雑で、LiveViewパターン特有の考え方を習得する必要がある
WebSocketが不要なら「SSE」という選択肢
双方向通信が不要な場合(通知、ライブフィード、AIのトークンストリームなど)は、Server-Sent Events(SSE)がより低コストな選択肢になる。SSEはHTTPの片方向チャネルで、インフラが単純でスケールしやすい。
htmxにはSSE拡張があり、以下のように属性を宣言するだけで使える。
<div hx-ext="sse" sse-connect="/updates" sse-swap="message">
Real-time content appears here
</div>
ただしSSEには制限もある。通信はサーバーからクライアントへの一方向のみで、クライアントが何かを送りたい場合は別途HTTPリクエストが必要になる。チャットや共同編集など双方向のやり取りが多い用途にはWebSocketの方が適している。
記事の結論はシンプルだ。双方向・低レイテンシが必要ならWebSocket、サーバーからの一方的な配信で足りるならSSE、リクエスト/レスポンスで十分ならHTTP over htmx。JSONを捨ててHTMLを直接送る発想を選ぶかどうかは、プロジェクトの要件次第である。
詳細はHTML over WebSockets: real-time SPAs with barely any JavaScriptを参照していただきたい。