Skip to content

Repository files navigation

🏠 WorthIt — Singapore Property Value Detective

Instantly check if a Singapore HDB resale flat or private property is fairly priced based on real transaction data from data.gov.sg and URA.

🔗 Live site: worthit.canlah.app

A free tool for Singapore homeowners and buyers — check HDB resale prices, condo transaction history, price trends, and fair-value estimates for any town, district, or project.

Check My Price: search any HDB postal code, enter an asking price, and get a 0–100 Deal Score with a fair-value range from storey-adjusted comparable sales nearby — flat type, size, floor, and remaining lease are inferred automatically from the block's transaction history.

Architecture

WorthIt uses a split architecture:

  • Frontend: Static files (public/) served via Cloudflare Pages (or node server/index.js locally)
  • API Server: Node.js/Express on Fly.io with SQLite database
  • public/config.js auto-detects environment and points API calls to the right backend

Quick Start (Local Development)

1. Install Dependencies

npm install
pip install requests pyproj   # for data download scripts

2. Download Data

npm run download-hdb           # HDB transactions (~230K records)
# npm run download-ura         # Optional: URA private property data (requires URA_API_ACCESS_KEY)

This downloads HDB resale transaction records from data.gov.sg into server/db/resale.db.

3. Start the Server

npm start

Open http://localhost:3000 in your browser. Express serves both the frontend and API.

Deploy to Fly.io (API Server)

Prerequisites

npm install -g flyctl
fly auth login

First-time Setup

fly launch           # Follow prompts (choose region, set app name)
fly secrets set ONEMAP_TOKEN=your_token_here   # optional, for geocoding

Deploy

fly deploy

Seed the Database

The Fly.io free tier (256MB RAM) is too small to run the Python download scripts. Instead, build the database locally and upload it:

# 1. Build database locally
python scripts/download_data.py
python scripts/download_ura_data.py   # optional

# 2. Upload to Fly.io volume
fly ssh sftp put server/db/resale.db /data/resale.db

# 3. Restart the machine to pick up the new DB
fly machine restart <machine-id>

Verify

curl https://<your-app>.fly.dev/api/status
# Expected: {"status":"ok","total_transactions":370252,"latest_month":"2026-05",...}

Debugging Commands

Check server logs

fly logs                  # live log stream
fly logs --no-tail        # recent logs only

Check machine status

fly machines list         # list all machines and their states
fly machine status <id>   # detailed status of a machine

SSH into the machine

fly ssh issue             # one-time: create SSH certificate
fly ssh console           # open a shell on the machine

Inside the SSH shell:

ls -la /data/             # check database file exists
ls -la /app/              # check deployed files
curl localhost:8080/api/status   # test API from inside the machine
cat /app/server/index.js | head  # verify deployed code

Upload / Download files

fly ssh sftp put local_file.txt /data/remote_file.txt    # upload
fly ssh sftp get /data/resale.db ./downloaded.db          # download

Restart

fly machine restart <machine-id>    # restart a specific machine
fly machines restart                # restart all machines

Common Issues

Problem Cause Fix
503 Database not ready No DB file on volume Run download scripts locally, then fly ssh sftp put
OOM kill on SSH Not enough RAM for Python Build DB locally and upload via SFTP
SSH auth failed No SSH certificate Run fly ssh issue then fly ssh establish
no_database status DB missing at /data/resale.db Upload via SFTP

Update Data (Monthly)

# Re-download latest data locally
python scripts/download_data.py

# Re-upload to Fly.io
fly ssh sftp put server/db/resale.db /data/resale.db
fly machine restart <machine-id>

Deploy Frontend to Cloudflare Pages

CLI Deploy (Recommended)

npx wrangler pages deploy public --project-name=worthit

First run will prompt browser authentication. This uploads public/ and gives you a live URL (e.g., https://worthit.pages.dev).

Alternative: GitHub-connected (Auto-deploys on push)

  1. Push code to GitHub
  2. Go to dash.cloudflare.com → Workers & Pages → Create
  3. Connect your GitHub repo
  4. Settings:
    • Framework preset: None
    • Build command: leave empty
    • Build output directory: public
  5. Deploy

Update Frontend

# Re-deploy after changes
npx wrangler pages deploy public --project-name=worthit

The frontend auto-connects to the Fly.io API via config.js.

Custom Domain Setup

The frontend is live at worthit.canlah.app. Here's how it was configured:

Step 1: Add domain to Cloudflare

  1. Go to dash.cloudflare.comAdd a site
  2. Enter your root domain (e.g., canlah.app) → Continue
  3. Select Free plan → Continue
  4. Review DNS records → Continue
  5. Note the two nameservers Cloudflare provides

Step 2: Update nameservers at domain registrar

  1. At Porkbun (or your registrar) → Domain Management → Authoritative Nameservers
  2. Replace with Cloudflare's nameservers
  3. Wait for propagation (minutes to a few hours)
  4. Cloudflare emails you when active

Step 3: Add custom domain in Cloudflare Pages

  1. Go to Cloudflare Dashboard → Pages → worthit → Custom domains
  2. Click Set up a custom domain
  3. Enter worthit.canlah.app
  4. Cloudflare automatically creates the CNAME record and provisions SSL
  5. SSL is automatic (Universal SSL) — no extra steps needed

Verify

curl -I https://worthit.canlah.app
# Should return HTTP 200 with Cloudflare headers

API Endpoints

Endpoint Description
GET /api/status Database status and record count
GET /api/towns List all HDB towns + district labels
GET /api/flat-types List all flat types
GET /api/resolve?q= Resolve town name or postal code
GET /api/area-overview?town= Main HDB market overview endpoint (accepts lat/lng for 500m radius)
GET /api/valuation?postal=&price= Deal Score + fair value from storey-adjusted nearby comps
POST /api/geocode Batch geocode addresses
POST /api/feedback In-app feedback (rate-limited, separate feedback.db)
GET /api/private/projects?q= Search private property projects
GET /api/private/project-overview?project= Private project details
GET /api/private/property-types List private property types
GET /api/private/district-summary?districts= District aggregate stats
GET /api/private/district-overview?district= Full district overview
GET /api/nearby-hdb?lat=&lng= Nearby HDB transactions + private projects (800m)
GET /api/seo/metadata?route= Bot metadata for edge-function injection
GET /api/seo/sitemap Sitemap URLs (consumed by Cloudflare edge function)

Project Structure

WorthIt/
├── scripts/
│   ├── download_data.py         # HDB data download (data.gov.sg)
│   └── download_ura_data.py     # URA private property data
├── server/
│   ├── index.js                 # Express API server
│   └── db/
│       └── resale.db            # SQLite database (generated)
├── public/
│   ├── index.html               # Main SPA
│   ├── config.js                # API base URL config
│   ├── css/
│   │   └── styles.css
│   └── js/
│       ├── api.js               # API client
│       ├── app.js               # Main app logic
│       ├── charts.js            # Chart.js configs
│       └── map.js               # Leaflet map
├── Dockerfile                   # Fly.io Docker config
├── fly.toml                     # Fly.io app config
├── package.json
├── requirements.txt
└── README.md

Tech Stack

  • Data: data.gov.sg HDB Resale Flat Prices + URA private property transactions
  • Pipeline: Python 3 + SQLite
  • Backend: Node.js + Express + better-sqlite3
  • Frontend: Vanilla JS + Tailwind CSS + Chart.js + Leaflet
  • Hosting: Fly.io (API) + Cloudflare Pages (frontend)

Data Sources

Disclaimer

Prices shown are indicative and based on historical transaction data. They should not be considered as professional valuations. Always consult a licensed valuer or property agent for formal advice.

About

WorthIt — free Singapore HDB & condo resale price explorer. 370K+ transactions, deal scoring, price trends, and BTO launch comparisons. Built on data.gov.sg and URA open data.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages