DreamとTyXMLの連携:JSX風構文で実現する型安全なHTML生成

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仕様の制約を直接反映しています。例えば:

  • ブロック要素とインライン要素の区別
  • 特定タグ専用の属性(hrefa要素のみなど)
  • 必須子要素の存在確認

これらにより、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/ディレクトリには、より実践的な活用例が収められています。これらを参照することで、フォーム処理や動的ルーティングとの組み合わせ方を確認できます。

タグ: Dream TyXML OCaml JSX 型安全HTML

9月12日 07:39 投稿