TL;DR for the Impatient
Astro (hybrid SSR/SSG) + Supabase (auth, database, storage) + Cloudflare Pages (hosting) + Stripe (payments) + DeepSeek via OpenRouter (AI). Total monthly cost: roughly one coffee. No React state management. No microservices. No Kubernetes. Just vibes.
Oh, how cute — you scrolled past the TL;DR? I thought you don’t want to read this piled-up slop. lmao.
The Full Picture
I wanted to build RelationMap.io as fast as possible with as little money as possible. No VC funding, no team, no fancy infra. Just a guy with a laptop and a dream of mapping character relationships without losing his mind.
Here’s everything that powers this thing, and why I picked it.
Frontend: Astro
I went with Astro because it lets me do both:
- SSG (Static Site Generation) for blog posts like this one — pre-rendered at build time, cached by CDN, fast as hell, great for SEO
- SSR (Server Side Rendering) for user-generated content — when someone shares a relationship map, it gets its own URL with dynamic data, proper Open Graph tags, the works
No SPA routing. No client-side hydration for pages that don’t need it. The editor page (/edit/) loads vanilla JavaScript directly — no React, no Vue, no framework. Just a canvas, some event listeners, and a lot of querySelector.
Why not React? Because I didn’t need it. The editor is essentially a D3-powered SVG canvas with drag-and-drop. Adding React would mean adding a build step, a bundler config, state management, and 200KB of JavaScript just to render some circles and lines. No thanks.
Hosting: Cloudflare Pages
Cloudflare Pages gives me:
- Global CDN for free
- Edge-rendered SSR pages
- Custom domain with free SSL
- Generous free tier that I’ll probably never outgrow
The deploy process is simple: astro build → wrangler pages deploy. That’s it. No Docker, no CI/CD pipeline (yet), no deployment YAML files. Just push and pray.
Backend: Supabase (the Real MVP)
Supabase is doing all the heavy lifting. Honestly, this project wouldn’t exist without it.
Auth
- Google and GitHub OAuth for sign-in
- Magic link as fallback
- Session management built-in
- I didn’t write a single line of auth code. Not one.
Database (Postgres)
- Maps, nodes, edges, user profiles — all in simple relational tables
- Row Level Security (RLS) so users can only see their own stuff
- Realtime subscriptions for collaborative editing (coming soon… maybe)
Storage
- Exported PNG images go here
- Cover images for shared maps
- Simple bucket with public/private access
Edge Functions
- Server-side logic that I don’t want in the client
- Stripe webhook handling
- AI prompt processing (so API keys never touch the browser)
I know it’s a lot of eggs in one basket. If Supabase goes down, so does RelationMap. But for a side project? The convenience is unbeatable.
AI: OpenRouter + DeepSeek
Here’s how the “AI-powered generation” actually works:
- User pastes a paragraph of text (a plot summary, a wiki page, their own writing)
- I send it to OpenRouter with a prompt like “extract all characters and their relationships from this text”
- OpenRouter routes it to DeepSeek V3.2
- DeepSeek returns structured JSON: characters with names/descriptions, and relationships with labels
- I parse the JSON and throw it onto the canvas
That’s literally it. No fine-tuning, no embeddings, no vector database, no RAG pipeline. Just a well-crafted prompt and a cheap model.
Why DeepSeek? Because it costs like $0.001 per request and it’s surprisingly good at extracting structured data from messy text. I tried GPT-4 first — works great, costs 100x more. For this use case, DeepSeek is more than enough.
Why OpenRouter? Because I can switch models with one line of config. If DeepSeek dies tomorrow, I point it at Claude or GPT-4o-mini and nothing else changes.
Payments: Stripe
Stripe handles the paywall. It’s a one-time purchase, not a subscription — because I hate subscriptions and I assume you do too.
The integration is minimal:
- Stripe Checkout for the payment flow
- Webhook to mark the user as “paid” in Supabase
- That’s it. No plans, no tiers, no “upgrade to Enterprise for team features”
Will I add subscriptions later? Maybe. But for now, “pay once, use forever” feels more honest.
The Canvas / Editor
This is where most of the code lives, and it’s all vanilla JavaScript:
- SVG rendering via D3.js for the graph layout
- Drag-and-drop — custom implementation, no library
- Undo/redo — a simple command stack pattern
- Export — html2canvas for PNG, native JSON serialization for data
- Popover menus — hand-rolled, because every popover library is either too heavy or too opinionated
The editor is about 15 JS files, each handling one thing. No bundler, no module system (just plain <script> tags), no build step for the editor itself. It loads fast because there’s nothing to compile.
Is it the most elegant architecture? No. Does it work? Yes. Will I refactor it when it becomes a problem? Also yes. But not today.
What I Didn’t Use (and Why)
| Technology | Why I Skipped It |
|---|---|
| React/Vue/Svelte | Overkill for a canvas-based editor |
| Redux/Zustand | No complex state to manage |
| Tailwind CSS | I like writing CSS. Fight me |
| NextJS/Remix | Astro does SSR/SSG hybrid better for my use case |
| MongoDB | Relational data is… relational. Postgres wins |
| AWS/GCP | Free tier too confusing, billing too scary |
| Docker | Nothing to containerize (yet) |
| TypeScript (mostly) | Config is TS, editor is JS. Gradually migrating |
Cost Breakdown
Here’s what I actually pay per month:
| Service | Cost | Notes |
|---|---|---|
| Cloudflare Pages | $0 | Free tier |
| Supabase | $0 | Free tier (500MB database, 1GB storage) |
| OpenRouter / DeepSeek | ~$2 | Depends on usage, usually less |
| Domain (relationmap.io) | ~$3/month (amortised) | Bought annually |
| Stripe | 2.9% + 30c per transaction | Only when someone actually pays |
| Total | ~$5/month |
And that’s mostly the domain name and endless sleepless nights to maintain and vibe-code with no time to wash my grizzly greasy hair — yes I know, non-native speaker here, keeping it to pass the AI detection test.
Oh please, I shall get an IPO for this shit.
Lessons Learned
-
Free tiers are your best friend. Supabase + Cloudflare = a full-stack app for $0/month until you hit real scale.
-
You don’t need a framework for everything. The editor works fine with vanilla JS. The blog works fine with Astro’s built-in markdown. Not everything needs to be a component.
-
AI is cheaper than you think. DeepSeek via OpenRouter costs fractions of a cent per request. The expensive part isn’t the API call — it’s writing a good prompt.
-
Ship first, optimise later. The codebase has zero tests, inconsistent naming, and at least three TODO comments that say “fix this later.” It works. Users don’t care about your code quality. They care about whether the tool does what they need.
-
One-time purchases > subscriptions for small tools. Users are more willing to pay $5 once than $3/month forever. And honestly, it’s less guilt on my end too.
What’s Next
- Collaborative editing (Supabase Realtime)
- Template library (pre-made charts for popular shows/books)
- Better AI prompts (multi-turn conversation for refining the chart)
- Mobile-friendly editor (currently desktop-only, sorry)
- Maybe… maybe a mobile app. Maybe.
Check the dev log for the latest updates, or just go make a chart.
This post will probably be outdated in a week because I keep changing things. That’s the beauty of a side project — nobody can stop me.