{
  "version": "https://jsonfeed.org/version/1",
  "title": "Programming on Nathan Heller",
  "icon": "https://avatars.micro.blog/avatars/2026/01/1859195.jpg",
  "home_page_url": "https://blog.naheller.com/",
  "feed_url": "https://blog.naheller.com/feed.json",
  "items": [
      {
        "id": "http://nxte.micro.blog/2026/05/25/tracking-books-with-webhooks/",
        "title": "Tracking books with webhooks",
        "content_html": "<p>One of my favorite things about small projects is the way in which their constraints can inspire unexpected solutions. You might expect a simple design to limit the room for creativity, but in my experience the opposite is often true.</p>\n<p>With this project I had the good fortune to stumble into a solution that 1) taught me something new, and 2) leveraged each puzzle piece&rsquo;s natural features in a way that preserved the minimalism I was going for.</p>\n<p>In this case the puzzle pieces were Netlify (with its build hooks) and Google Forms (with its Apps Script platform integration).</p>\n<h2 id=\"a-book-tracker\">A book tracker</h2>\n<p>The idea was to build a quick and easy way for my wife and I to log the books we finish in a shared list. We didn&rsquo;t need a full social media platform like Goodreads, and in fact the UI cruft there added friction to the book entry process. Likewise our phones&rsquo; Notes app was too simplistic, missing basic features like sorting and filtering. But something relatively low-tech seemed best, given our limited requirements and reluctance to buy into another app or ecosystem.</p>\n<p>So we landed on a spreadsheet, which appealed to me as a data store, but fell short as a view/edit layer, since we mostly planned to be using the tool on our phones. Pinch-zooming around a spreadsheet on a 6-inch screen isn&rsquo;t much fun. Google Forms tied the data entry side together as it had decent mobile UX and could be set up to feed responses into a spreadsheet. Only the view layer remained.</p>\n<p>Lately I&rsquo;ve been reaching for vanilla JavaScript and HTML more often when building simple websites. After all, isn&rsquo;t React just the Goodreads of frontend frameworks? That&rsquo;s not the low-tech spirit we&rsquo;re looking for here. We need only fetch our books from the Google Sheets API and display them in a single column list. Add some basic sort/filter options and call it a day. That has Web 1.0 written all over it.</p>\n<p>In order to hide my API key from prying eyes, I decided to make the call server-side, which is like, even more Web 1.0 since the earliest browsers didn&rsquo;t even run JavaScript.</p>\n<p>For hosting I reached for the friendly and familiar (and free) Netlify, which has served me well over the years. In effect I&rsquo;d write my own barebones static site generator in a single JS file, which would insert the book data into an HTML string and write it to an <code>index.html</code> file. Netlify would run that JS file at build time and serve the HTML.</p>\n<h2 id=\"a-build-hook\">A build hook</h2>\n<p>For a while I thought I&rsquo;d finished. I&rsquo;m not proud to admit how long I scratched my head when newly added books didn&rsquo;t appear on the site. Then I pushed a small code change to Github, which triggered a new Netlify build, and suddenly the books appeared. Of course. The Google Sheets API fetch only happened at build time. I needed an automated way to tell Netlify whenever a new book was added to the spreadsheet.</p>\n<p>Another admission: in my 8 years as a frontend engineer, I&rsquo;ve never really taken the time to learn about webhooks. I&rsquo;d only heard about them in passing and had a vague notion that they could connect disparate web services. Since educating myself on the concept, I&rsquo;m glad to report that I wasn&rsquo;t too far off the mark. Red Hat sums it up:</p>\n<blockquote>\n<p>A webhook is a lightweight, event-driven communication that automatically sends data between applications via HTTP</p>\n</blockquote>\n<p>I poked around my Netlify dashboard hoping to find something useful. Thankfully they&rsquo;ve thought of just about everything (this is not a sponsored post), and I eventually lit upon a build hooks section. It promised to give me a unique URL that I could use to trigger a build, and by golly, it did just that.</p>\n<pre tabindex=\"0\"><code>curl -X POST -d &#39;{}&#39; https://api.netlify.com/build_hooks/&lt;my-hook-id&gt;\n</code></pre><p>With hook in hand, I headed back over to the Googleplex to figure out whether I could make it sing my particular tune.</p>\n<h2 id=\"an-app-script\">An app script</h2>\n<p>The Google <del>minefield</del> suite of products and services is a daunting place to find yourself. On the developer side, it feels like every other feature is buried behind layers of usage agreements, consent screens, and granular credentials that rival the <del>worst</del> best AWS has to offer.</p>\n<p>Thankfully the Apps Script page is mercifully minimal and requires only a modest amount of documentation to comprehend. Once my reading had convinced me it was possible, I set about creating a trigger that would run whenever my Google Form was used to submit a new book. My little function amounted to no more than a fetch request:</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-javascript\" data-lang=\"javascript\"><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">function</span> <span style=\"color:#a6e22e\">onFormSubmit</span>() {\n</span></span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">const</span> <span style=\"color:#a6e22e\">WEBHOOK_URL</span> <span style=\"color:#f92672\">=</span> <span style=\"color:#e6db74\">&#39;https://api.netlify.com/build_hooks/&lt;my-hook-id&gt;&#39;</span>;\n</span></span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">var</span> <span style=\"color:#a6e22e\">options</span> <span style=\"color:#f92672\">=</span> {\n</span></span><span style=\"display:flex;\"><span>\t\t<span style=\"color:#e6db74\">&#34;method&#34;</span><span style=\"color:#f92672\">:</span> <span style=\"color:#e6db74\">&#34;post&#34;</span>,\n</span></span><span style=\"display:flex;\"><span>\t\t<span style=\"color:#e6db74\">&#34;headers&#34;</span><span style=\"color:#f92672\">:</span> {\n</span></span><span style=\"display:flex;\"><span>\t\t\t<span style=\"color:#e6db74\">&#34;Content-Type&#34;</span><span style=\"color:#f92672\">:</span> <span style=\"color:#e6db74\">&#34;application/json&#34;</span>\n</span></span><span style=\"display:flex;\"><span>\t\t},\n</span></span><span style=\"display:flex;\"><span>\t\t<span style=\"color:#e6db74\">&#34;payload&#34;</span><span style=\"color:#f92672\">:</span> {},\n</span></span><span style=\"display:flex;\"><span>\t};\n</span></span><span style=\"display:flex;\"><span>\t<span style=\"color:#a6e22e\">UrlFetchApp</span>.<span style=\"color:#a6e22e\">fetch</span>(<span style=\"color:#a6e22e\">WEBHOOK_URL</span>, <span style=\"color:#a6e22e\">options</span>);\n</span></span><span style=\"display:flex;\"><span>}\n</span></span></code></pre></div><p>I then set up a new trigger that called this function on form submit. Interestingly, the other event type option was on form <em>open</em>. Seems like jumping the gun there a bit, but to each their own.</p>\n<p>Once I&rsquo;d wired this up I was finally able to trigger new website builds whenever a book was added. This wasn&rsquo;t instantaneous, but a dozen seconds is perfectly adequate considering we rarely rush right over to check. I&rsquo;ll admit I did rush over a few times when I first got it working. It&rsquo;s cool, okay? It works! And not a dime spent</p>\n",
        "date_published": "2026-05-25T16:00:34+07:00",
        "url": "https://blog.naheller.com/2026/05/25/tracking-books-with-webhooks/",
        "tags": ["Programming"]
      }
  ]
}
