OCamlのWeb開発において、HTML生成の安全性を確保することは重要な課題です。DreamフレームワークとTyXMLを組み合わせることで、JSXに似た記述的な構文を保ちながら、コンパイル時の型検証を活用したWebページ構築が可能になります。
アプローチの特徴
従来の文字列連結によるHTML生成では、実行時まで構造上の不備に気づけません。TyXMLを採用すると、以下の恩恵が得られます:
- 構造の静的検証:タグの入れ子関係や必須属性の欠落をコンパイル段階で検出
- ドメイン固有言語:OCamlの構文拡張を活用した直感的な記述
- Dreamとの親和性:HTTPレスポンス生成との自然な統合
最小構成の実装
依存関係を整えた後、以下のような構成で動作させられます:
let page_content recipient =
let module H = Tyxml.Html in
H.div ~a:[H.a_class ["container"]]
[ H.header
[ H.h1 [H.txt "Welcome"] ]
; H.main
[ H.p [H.txt "Hello, "; H.txt recipient; H.txt "!"] ]
]
let render_document content_node =
let module H = Tyxml.Html in
H.html
(H.head (H.title (H.txt "Demo")) [])
(H.body [content_node])
let serialize html =
Format.asprintf "%a" (Tyxml.Html.pp ()) html
let () =
Dream.run ~interface:"0.0.0.0" ~port:8080
@@ Dream.logger
@@ Dream.router [
Dream.get "/" (fun _ ->
let body = "visitor" |> page_content |> render_document |> serialize in
Dream.html body);
]
この実装では、page_contentが受け取った文字列を含むDOM断片を生成し、render_documentで完全な文書に包み込んでいます。各関数の返り値が型によって制約されるため、誤ってhead要素をbody内に配置するような論理ミスは型エラーとして拒否されます。
型システムによる保証の具体例
TyXMLの型定義は、HTML仕様の制約を直接反映しています。例えば:
- ブロック要素とインライン要素の区別
- 特定タグ専用の属性(
hrefはa要素のみなど) - 必須子要素の存在確認
これらにより、IDEの補完機能と連携して安全なコーディングが促進されます。
コンポーネント化のパターン
再利用可能な部品を形成する際は、関数の合成を活用します:
let navigation items =
let module H = Tyxml.Html in
H.nav [H.ul (List.map (fun (label, path) ->
H.li [H.a ~a:[H.a_href path] [H.txt label]]
) items)]
let layout ~title ~navigation content =
let module H = Tyxml.Html in
H.html
(H.head (H.title (H.txt title)) [])
(H.body [navigation; H.main content])
このように、純粋関数として定義された部品を組み合わせることで、テスト容易性の高い構成が実現します。
大規模ページへの対応
応答サイズが大きくなる場合、Dreamのストリーミング機能と組み合わせて段階的な出力が可能です:
Dream.stream ~headers:[("Content-Type", "text/html; charset=utf-8")]
(fun output ->
let%lwt () = output (serialize_partial header) in
let%lwt () = output (serialize_partial main_content) in
output (serialize_partial footer))
この手法により、クライアント側での早期レンダリング開始とサーバー側のメモリ効率の両立が図れます。
導入の手順
プロジェクトへの追加はduneファイルの編集から始まります:
(executable
(name server)
(libraries dream tyxml))
リポジトリ内のexample/w-tyxml/ディレクトリには、より実践的な活用例が収められています。これらを参照することで、フォーム処理や動的ルーティングとの組み合わせ方を確認できます。