Display dynamic JSON data in the Obsidian Digital Garden

To include dynamic content in my Digital Garden I rely on HTML iframes. To display data from a JSON file data.json

{
  "artist": "Luude",
  "title": "Down Under - feat. Colin Hay"
}

stored on a server I use the following HTML code.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Music Track</title>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> </head>
<body>
  <h1>Music Track</h1>
  <p id="musicTrack"></p>
  <script>
    $(document).ready(function() { 
      $.getJSON('https://server-name/data.json', function(track) {
        var trackHtml = '<strong>Title:</strong> ' + track.title + '<br><strong>Artist:</strong> ' + track.artist;
        $('#musicTrack').html(trackHtml);
      })
      .fail(function() {
        console.log("An error has occurred."); 
      }); 
    }); 
  </script>
</body>
</html>

This HTML code can live on (another) server and is included in an Obsidian node via

<iframe src="https://server-name/track.html" width="100%></iframe>

When I publish this note with the Digital Garden plugin, the HTML code stays intact. When someone browses to the page, the iframe is triggered and includes the HTML page which in turn reads the JSON file.

If the HTML code should be accessible on the same server that the Digital Garden is running, there's a way to add static HTML sites to the Digital Garden. If the JSON file that is accessed is still hosted on another server, you have to set the required Cross-Origin Resource Sharing (CORS) attributes. To do this for nginx, add the following to the server configuration on the website that hosts data.json:

server {
    location / {
        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Allow-Origin' 'https://_digital-garden-url_';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
            add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
            add_header 'Access-Control-Max-Age' 1728000;
            add_header 'Content-Type' 'text/plain; charset=utf-8';
            add_header 'Content-Length' 0;
            return 204;
        }
        if ($request_method = 'POST') {
            add_header 'Access-Control-Allow-Origin' 'https://_digital-garden-url_' always;
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
            add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
        }
        if ($request_method = 'GET') {
            add_header 'Access-Control-Allow-Origin' 'https://_digital-garden-url_' always;
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
            add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
        }
    }
}

If the web site is running behind a reverse proxy, omit the server directive and add proxy-pass.

location /data.json {
  [..]
  // code as above
  [..]
  proxy_pass http://__server-name__/data.json
}