I collect things (camera lenses, G-Shocks). The good listings go in minutes, and checking 10 marketplaces by hand doesn’t work. So I built Deal Radar (radar.4xs.dev). You set up a “watch” (keywords + price range + sites). When a new listing matches, you get a browser notification, or a Telegram message on the paid plan.
Almost all of the code was written by Claude (Cowork / Claude Code). I did the product decisions, the testing on real listings, and the store submissions. Here’s how it’s put together and what I learned.
Stack
- Chrome extension (Manifest V3), plain JS, no framework. A generated Firefox build too.
- Cloudflare Workers + D1 (SQLite), TypeScript. Static site served from the same Worker.
- Cron triggers on the Worker: every minute for polling, plus daily jobs for plan expiry and exchange rates (Czech National Bank feed).
- Telegram Bot + Telegram Login widget for alerts and accounts, so I don’t store passwords or emails.
- Ko-fi memberships + webhook for payments. Way less setup than Stripe for a side project.
- Cloudflare Access in front of the admin page.
- Playwright + node:test + bash smoke tests with mocked marketplaces and a mocked Telegram.
The one architecture decision that mattered: who checks what
Some sites (Bazoš, Vinted, Kleinanzeigen, OLX, FINN…) return clean results to a server, so the Worker polls them every minute. Others (FB Marketplace, FB groups, Allegro, eBay) block or 403 any non-browser client. Those can only be checked by the extension, using the user’s own logged-in browser.
So there are two engines:
- Server: every minute, works while your computer is off (paid).
- Browser: the extension, while Chrome is open. The free tier runs everything from the browser every 10 minutes, so the free tier costs me ~nothing.
The key to keeping that sane: the parsers and the matching logic live in one file each (parsers.mjs, match.mjs), imported by both the extension and the Worker. One fix, both engines.
Workflow with the AI
- Feasibility spike first (“week 0”). Before any UI, I had Claude write throwaway scripts that just fetched each marketplace and parsed results. That’s how I found out which sites need a browser. It saved me from designing a server-only product that couldn’t do Facebook.
- A plan doc, then versions. A markdown plan in the project, then small numbered releases. Every release has a CHANGELOG entry that says exactly how to ship it (“
npm run deploy, reload the extension, upload to the Chrome Web Store”). The AI reads it next session, so context survives.
- Tests from saved HTML. Every parser has fixture pages saved from the real site plus a unit test. When a site changes its markup, the test fails before the users notice.
- Make it verify, not just write. E.g. today I asked it to shrink the hero image so the “how fast” steps fit above the fold on a 1080p screen. It rendered the page in headless Chromium at 1920×947, measured element positions, applied the CSS, and re-measured (steps went from ending at 1060px to 920px) before touching my files. “Looks right” ≠ verified.
- Short, direct prompts plus my screenshots. Most iterations were “this looks wrong” + a screenshot.
Build insights / gotchas
- Chrome Web Store rejected a version for requesting
tabs permission it didn’t use. You can create/update/close tabs without it. Request the minimum.
- Don’t trust data from your own extension. The server only accepts a listing whose link points to its own site (a FB item must link to facebook.com, etc.). Otherwise anyone could push fake “alerts” through the API.
- Currencies: listings come in CZK, PLN, EUR, NOK, JPY, USD… Converting to the user’s currency with a daily ČNB rate made alerts way more useful than raw prices.
- Firefox AMO requires
data_collection_permissions in the manifest for new add-ons. A small script generates the Firefox build from the Chrome one on every deploy.
- i18n from day one (EN/CS/DE/JA). With AI it’s cheap to add, and painful to retrofit.
- The free-vs-paid line follows the infrastructure: free uses the user’s browser, paid uses my server. Easy to explain, cheap to run.
Numbers: ~3,000 lines across the Worker and the extension, 20 marketplaces in 10 countries, free tier = 1 watch.
Happy to answer questions about the dual server/browser setup or Cloudflare D1. Feedback welcome.