2026-03-15
BeerBoss.ca: Refreshing my first side project
Rewriting my first ever side project from scratch, six years later.
This weekend I rewrote BeerBoss.ca from scratch. BeerBoss was my first ever side project, which I started back in 2020 to find the cheapest beer available in Ontario at The Beer Store. The project started with a question: "What is the absolute cheapest beer in Ontario?"
The original version worked fine, but it was over-engineered for what it actually did. When I first built it, I was just trying to experiment with new technologies. At this point, I just want it to be simple.
The Old Architecture
The original stack: a Dockerized Express backend and a Dockerized React frontend, deployed to a VPS.
The backend had exactly one endpoint: fetch the beer data. One GET request that returned JSON. For that, I had a full Node.js server running in a Docker container, sitting on a VPS I was paying ~$40 a year for.
I built it that way because I was exploring new technologies. Self-hosting on a VPS is actually a great experience. You SSH in, set up nginx, manage your own deployments, and get a real feel for how things work under the hood. At under $5/month it's also pretty cheap. Pieter Levels famously runs his entire business (hundreds of thousands a month in revenue) off a single VPS. But I don't even need a backend. A VPS is overkill for serving a static JSON file.
The New Architecture
The new version is a Next.js static export, hosted on Cloudflare Pages for free.
All ~2,500 beer purchasing options live in a single beers.json file in the repo. The whole frontend is a static site. No backend server at all. Sorting, filtering, and searching all happen client-side, which is completely fine for a dataset this size.
How I Scrape 2,500+ Purchasing Options in Under 3 Seconds
This is the cool part!
The original scraper worked by crawling the old Beer Store website: grab every beer URL from the homepage, visit each one individually, and scrape the price. With 700+ beers and over 2,500 purchasing options (pack sizes, cans, bottles, kegs), this took a while. If you go too fast, you get rate-limited. I was running it from my laptop with no proxies.
A few years ago, The Beer Store redesigned their website. My script broke. When I went to fix it, I poked around the network tab and discovered they had migrated to Algolia for their product search. Algolia is a hosted search API and the credentials it uses on their frontend are, by design, meant to be public.
The new scraper is a single Python file. It makes a POST request to their Algolia endpoint with an empty query and hitsPerPage=1000, then paginates through the results.
Each response gives back a page of up to 1,000 beers with everything I need: name, price, sale price, ABV, format, quantity, country, and a link back to the product page. A few paginated requests and I have the entire catalogue. The whole thing runs in under 3 seconds.
If The Beer Store ever migrates off of Algolia, I'll have to find another way.
The Dollars Per Drink Metric
One thing I compute that the Beer Store doesn't show you: dollars per standard drink of alcohol.
A standard drink in Canada is 17.75 mL of pure alcohol. That number comes directly from a standard can of beer: 355 mL × 5% = 17.75 mL. Given a beer's price, ABV, size, and quantity, I can calculate exactly how much you're paying per unit of alcohol:
def calculate_dollars_per_serving_of_alcohol(
price: float, size_ml: int, quantity: int, abv: float
) -> float:
"""Dollars per serving of alcohol (1 serving = 17.75 mL of pure alcohol)."""
return round(price / ((size_ml * quantity * (abv / 100)) / 17.75), 4)This is the core metric that BeerBoss is built around. You can sort by it to find the best value in the entire store.
Automating the Scraper
Before this rewrite, I ran the scraper manually somewhere between once a week and once every couple months. Data got stale if I forgot to update it. I kept meaning to automate it and never did.
Now it runs once a day via a GitHub Action. The action runs the Python script, commits the updated beers.json to the repo, and Cloudflare Pages re-deploys automatically.
Analytics
I had 3,200 users in 2025 and 5,100 page views. That's down 42% from 2024, which is a bigger drop than I can attribute to "people are drinking less." My best guess is that my Google rankings got a bit worse.

What's Next
A few things I'd like to add eventually:
Historical price tracking. I have six years of scraped data sitting around. It would be cool to show price trends over time.
Per-location inventory. Right now I'm scraping data from a single Toronto store. Prices and sales are pretty consistent across Ontario, but stock levels vary by location. It'd be nice to add an "In Stock" filter so you're not browsing beers that aren't available near you.
If you're in Ontario and looking for the best deal on beer, give it a try.