Today I built a small project using Supabase as a backend. The project is called VanityLib, and it’s a public directory of rare Bitcoin addresses — each one pre-generated with a readable prefix and paired with a private key. The user can browse them in a table and pick one. Once selected, the address should be removed from the database.
I started by signing up for Supabase and creating a new project. Then I created a PostgreSQL table named addresses
with the following schema:
CREATE TABLE addresses (
id bigint generated by default as identity primary key,
public_key text not null,
private_key text not null,
pattern text
);
I imported the data using a .csv
file containing rows like:
BTC,1Actor,1ActorRiBSCVn5yAFCB9mWf5eUb9TtirJh,5JJ2WpyHevdyqKzYapDjcDmZx7XDcgaSNFqUn8g9UbRdU2fxXVN
Only public_key
, private_key
, and pattern
were relevant. I uploaded the data via the Supabase UI.
On the frontend, I used plain HTML, CSS, and JavaScript. Supabase provides an ESM-compatible client via CDN:
import { createClient } from 'https://cdn.jsdelivr.net/npm/@supabase/supabase-js/+esm';
const supabase = createClient(SUPABASE_URL, ANON_KEY);
From there, I queried the addresses
table using .select()
and rendered the results in a table. Rarity is derived by inspecting the length of the pattern
column. I mapped it to a set of emoji symbols to represent the quality of the vanity prefix:
const rarityLevels = ['🪵', '🥄', '🔩', '🪙', '💎', '🧊', '🌌'];
const rarity = rarityLevels[Math.min(Math.max(patternLength - 3, 0), rarityLevels.length - 1)];
Private keys are hidden by default. Clicking the “Click to show” cell triggers a secondary Supabase query which fetches the corresponding private key via:
const { data } = await supabase
.from('addresses')
.select('private_key')
.eq('public_key', publicKey)
.single();
I also added pagination using .range(page * pageSize, (page + 1) * pageSize - 1)
and a couple of navigation buttons that let users load the next or previous page.
Next steps are to:
- Remove a row from the table when the private key is revealed (via
.delete()
), - Possibly replace the static site with a small Svelte or Astro frontend for better structure,
- Add serverless Supabase functions for rate limiting or ownership logging.
This was the first time I used Supabase seriously, and the experience was solid. The setup was fast, and the client API is straightforward. No backend code needed, just client-side logic and some access control rules in the Supabase UI.
I decided to not publish the project since it can be misused.