- Why? Over the years, I've discovered some great blogs here, so I thought it would be nice to have a filter for them.
- Why only Substacks then and not blogs more generally? Because it's easy to query for "substack.com" in the public HN Algolia API (see below). Plus, hnblogs was already taken :)
- What about substacks on custom domains then? At the bottom of the page there is a form to submit a custom domain. The site attempts to auto-validate that it is a Substack. If that fails, it is marked for manual review (which I'll do every once in a while). Approved ones will be included in the results.
- How? I use HN's Algolia API to fetch substack stories by searching for "substack.com" in the url. I do this via a CloudFlare worker that has 3 separate cron triggers: (1) fetches new stories every 10 minutes and saves them in a KV store. (2) runs 2 minutes after the first, computes hot stories from new ones by simulating HN's score algorithm, and stores in the KV. (3) Once every hour it fetches the best stories (historically, ordered by points) and saves. I store 300 new stories (10 pages of results) and 900 best (30 pages). The frontend is a static (Svelte 5, no Kit) site that just queries the worker and displays (paginated) results. For more details, check the public repo: https://github.com/ariroffe/hnsubstacks/
- Did you use AI to code this? Yes. I'd say that ~90% of the code was written by Claude and Gemini... all via chat! Full vibe coding feels too magical for me, and I don't want the robots touching my code. This took me about 4 full days of work to make (spread in many small slices). If I fully vibe coded it, it could probably have been done faster, but I wouldn't have learned as much, and would've had way less fun.
(Note: this is all running in a free instance of CF workers, so I had to optimize it a bit to run in under 10ms CPU time. I can't guarantee it won't get hugged to death, though)