Application Modules

Alternate Formats

Serve SVG, XML, or JSON entry points from app modules and trigger dynamic downloads using embedded scripts.

Alternate Formats (SVG, XML, JSON)

Besides HTML, modules can include alternate format renderings. The server automatically routes requests for the module’s endpoint to an entry point file (checked in order: index.html, index.xml, index.svg, index.json, index.js).

This enables serving other formats in browser rendering. For example, JavaScript can run inside an index.svg file to dynamically fetch data via the /graphql endpoint and construct a visualization directly in the browser:

<!-- app/index.svg -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 600">
  <script type="text/javascript"><![CDATA[
    async function renderGraph() {
      const response = await fetch('/graphql', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ query: '{ countNodes }' })
      });
      const result = await response.json();

      // Generate XML/SVG dynamically
      const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
      text.setAttribute("x", "20"); text.setAttribute("y", "40");
      text.textContent = "Total Graph Nodes: " + result.data.countNodes;
      document.documentElement.appendChild(text);
    }
    renderGraph();
  ]]></script>
</svg>

Generating XML Downloads

Browsers do not execute JavaScript inside generic XML files. If your module needs to dynamically generate and download a generic XML file (such as GraphML) without a visible HTML interface, you can serve an index.xml file containing an <svg> root element.

The browser recognizes the SVG namespace and executes the embedded <script>, turning the file into an “invisible launcher”. The script can fetch data, construct the desired XML payload, and immediately trigger a download or redirect. Because the file is parsed as strict XML, wrap the script contents in <![CDATA[ ... ]]> to prevent parsing errors from characters like < and &.

<!-- app/index.xml -->
<svg xmlns="http://www.w3.org/2000/svg">
  <script type="text/javascript"><![CDATA[
    async function generateXml() {
      // 1. Fetch data from GraphQL
      const response = await fetch('/graphql', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ query: '{ countNodes }' })
      });
      const result = await response.json();

      // 2. Construct the target XML payload
      const xml = `<?xml version="1.0" encoding="UTF-8"?>\n` +
                  `<report>\n` +
                  `  <totalNodes>${result.data.countNodes}</totalNodes>\n` +
                  `</report>`;

      // 3. Create a Blob and trigger a download
      const blob = new Blob([xml], { type: 'application/xml' });
      const url = URL.createObjectURL(blob);

      const a = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
      a.href = url;
      a.download = 'report.xml';
      a.click();

      setTimeout(() => URL.revokeObjectURL(url), 100);
    }
    generateXml();
  ]]></script>
</svg>