I wanted to see how far I could push a 0 Total Cost) Frontend: One static index.html file (HTML, CSS, vanilla JS) Database: Supabase (PostgreSQL on the free tier) Hosting: Netlify (static edge deployment) Geolocation: ipwho.is (free client-side IP lookup, CORS-enabled) Engineering for Zero Servers & Free-Tier Limits When building a live public counter, the default instinct is to log an event for every tap and use WebSockets to push live updates. On a free tier, that will kill your database in minutes. Here is how I designed around those constraints: 1. One Row Per Country (Not One Row Per Tap) Instead of inserting an event row per click, the PostgreSQL table is structured as: create table country_counts ( country_code text primary key , country_name text , count bigint default 0 ); The database table stays under ~200 rows forever. Whether the site gets 10 taps or 10,000 taps, query performance and storage remain completely flat. 2. Polling Instead of Realtime WebSockets Holding open a persistent WebSocket connection for every concurrent visitor drains Supabase free-tier connection limits rapidly. Instead, the frontend polls SELECT * FROM country_counts every 2 seconds . For a dark split-flap departure board aesthetic, a 2-second mechanical tick feels natural and keeps server load predictable. 3. Locking Down Writes with security definer Since there is no backend server, the browser talks directly to Supabase. To prevent anyone from opening the browser console and sending UPDATE country_counts SET count = 999999 , I revoked direct INSERT and UPDATE permissions on the table for anonymous users. All taps pass through a custom PostgreSQL function: create or replace function tap ( p_country_code text , p_country_name text ) returns bigint language plpgsql security definer as ; The public API key only has EXECUTE permission on tap() , making schema corruption impossible from the REST API. What’s Next? I’m currently exploring lightweight edge-rate-limiting to supplement the client-side localStorage spam check without adding backend server costs. I'd love your feedback on: The PostgreSQL schema and function design. How you handle rate-limiting on pure static/serverless setups! Drop a tap for your country and let me know what you think in the comments! 👇

How I built a live global scoreboard for $0 using Static HTML and Supabase published: true
makuna hatata

