A tool to make board games for Print & Play and Tabletop Simulator.
Designing board games is hard. It requires rapid iteration and the ability to make changes on-the-fly. CardGoose makes it easier with data-driven components and custom templates to build your ideas in minutes.
- Import data from Google Sheets & refresh for live updates
- Component layout editor for card design
- Deck preview to evaluate changes
- Custom images (direct upload to Cloudflare R2)
- TTS export (coming soon)
| Component | Service |
|---|---|
| Database | Supabase Postgres |
| Auth | Supabase Auth |
| Object storage | Cloudflare R2 |
| API | Express on Vercel Serverless |
| Frontend | Vite SPA on Vercel (app.cardgoose.com) |
| Landing | Cloudflare Workers |
- Node.js 20+
- pnpm 9+
- Docker (optional β local Postgres via Compose)
- Supabase CLI (recommended for local auth)
- Cloudflare account with R2 bucket (for asset uploads)
cp .env.local.example .env.local
cp frontend/.env.local.example frontend/.env.localFill in Supabase and R2 credentials. For local Supabase:
supabase start
# Copy API URL + anon key into .env.local and frontend/.env.localWith Docker Postgres (port 5433):
pnpm docker:up
pnpm migrate:deployOr point DATABASE_URL / DIRECT_URL at your Supabase local Postgres (port 54322).
pnpm dev:localOr separately:
pnpm dev:api
pnpm dev:frontendOpen the URL Vite prints (usually http://localhost:5173).
Do not set VITE_API_URL when using the Vite dev proxy (frontend/vite.config.ts forwards /api β localhost:3001).
- Create a project at supabase.com.
- Copy Project URL, anon key, and Postgres connection strings (
DATABASE_URLwith pooler,DIRECT_URLwithout). - Enable Google OAuth under Authentication β Providers if needed.
- Create bucket
cardgoose. - Create R2 API token with read/write on that bucket.
- Set
R2_ACCOUNT_ID,R2_ACCESS_KEY_ID,R2_SECRET_ACCESS_KEY,R2_BUCKET.
API project β root directory api, uses api/vercel.json:
DATABASE_URL,DIRECT_URLSUPABASE_URL,SUPABASE_ANON_KEYR2_*varsCORS_ORIGIN=https://app.cardgoose.com
Frontend project β root directory frontend, uses frontend/vercel.json:
VITE_SUPABASE_URL,VITE_SUPABASE_ANON_KEYVITE_API_URLβ your API Vercel URL
Run migrations against production once:
DATABASE_URL=... DIRECT_URL=... pnpm migrate:deploy:prodVercel auto-deploys from GitHub on push to main. CI runs lint, tests, and builds β see .github/workflows/ci.yml.
| File | Purpose |
|---|---|
.env.local.example |
Template β .env.local β API (Postgres, Supabase, R2) |
frontend/.env.local.example |
Template β frontend/.env.local β Vite (Supabase client) |
pnpm run format:check
pnpm -r run lint
pnpm test:all
pnpm --filter frontend build