Back to projects

Fullstack · Astro, Supabase, Node · 2026

Turathiyat

Turathiyat is a Riyadh dealer in rare banknotes, coins and stamps whose business lived inside a classifieds marketplace. I built the whole system: a scraper, an Arabic catalogue on the shop’s own domain, a phone-first owner panel and a marketplace sync. Every buy button starts a WhatsApp message with the piece’s reference code.

  • Astro
  • Supabase
  • TypeScript
  • Node.js
  • Sharp
  • RTL
  • SEO
  • GEO
  • JSON-LD
  • Netlify
My part
All of it
When
2026
Pages
About 130
Pieces
119, 315 photos
Status
Live
Turathiyat homepage: Arabic RTL storefront for rare Saudi coins and banknotes

Overview

Turathiyat (تراثيات) is a Riyadh dealer in rare banknotes, coins, stamps and heritage antiques whose whole business lived inside a classifieds marketplace. I built them a catalogue site on a domain they own. Astro renders roughly 130 Arabic, right-to-left pages (119 pieces, 315 photos) and ships about 1.3 KB of client JavaScript. Every buy button opens WhatsApp with a prefilled Arabic message carrying that piece’s permanent reference code, so a lead can be traced back to the page that produced it. The catalogue has since moved off a committed JSON file into a Postgres table read per request, behind a phone-first owner panel, so the shop can be edited from a phone instead of through a rebuild. A one-tap sync pulls the marketplace’s current inventory into a review queue the owner clears before anything reaches the live site, and sold pieces flip automatically so a live buy button can never ship on stock that’s gone.

DecisionCodes run from TRH-0001, are assigned once and never reused, because they end up in real conversations.

My role

I built all of it end to end: the scraper and data pipeline, the site, the content architecture, the SEO and GEO work, the analytics, the owner panel and its marketplace-sync review queue, and the deployment. Refreshing the catalogue is no longer a rebuild. A stepper pulls the marketplace a page at a time, proposes every change, and the owner applies or dismisses each one from a phone.

ConstraintThe marketplace is still where the inventory starts. The sync reads from it, and the owner approves every change.

What shipped

  • A Node scraper over the marketplace’s GraphQL API that fetches listings and photos and normalises them into one dataset.
  • An Astro static site with a homepage, catalogue, six category pages, a collector’s guide, FAQ, shipping and about pages, plus a page per piece, all prerendered.
  • A dataset that is the union of everything ever scraped, so a sold piece keeps its page, its URL and its accumulated rankings instead of vanishing.
  • Frozen reference codes (TRH-0001 and up) assigned once per piece and never reassigned, because they end up in real WhatsApp conversations.
  • Client-side Arabic search over a prebuilt JSON index, ranking available stock first, with no-result queries recorded as demand the inventory does not cover.
  • Build-time image work with Sharp: social cards and search thumbnails generated per listing, skipping whatever already exists so rebuilds stay cheap.
  • Deployed on Netlify with a custom domain, a build-time config module holding every deployment-specific value, and a first-party analytics proxy at the edge.
  • A phone-first owner panel on the site itself: sign in, edit a piece, add one, mark one sold, and freeze any field the sync should stop proposing changes to.
  • A one-tap sync with the marketplace that pulls its current inventory, diffs it against the catalogue, and queues every difference for the owner to approve or dismiss. Nothing reaches the live site unreviewed except a sold-state flip, which applies automatically so a live buy button can never ship on stock that’s gone.

DecisionNothing reaches the live site unreviewed. Only a sold-state flip applies on its own.

SEO & GEO

  • Product and Offer JSON-LD on all 119 listing pages, with real prices where they exist and a sold-out state on sold stock.
  • ItemList on the catalogue and category pages, FAQPage on the homepage and FAQ page, BreadcrumbList everywhere, and a Store node linking to the seller’s identity-verified marketplace profile, which ties a brand-new domain to an established reputation.
  • Titles written to differ deliberately from the marketplace’s, so the new pages are not fighting a far stronger domain over identical text.
  • Arabic URL slugs carrying the listing id as a stable prefix, so a seller editing a title can never break a URL. Slugs are lowercased, because the host redirects mixed-case paths and a canonical must not point at a redirect.
  • A robots.txt that allows AI crawlers by name (GPTBot, ClaudeBot, PerplexityBot, Google-Extended and others) rather than relying on a wildcard the opt-out gates ignore.
  • An llms.txt serving a plain-text inventory and reference summary, and a generated sitemap, so assistants can read the catalogue without rendering it.
  • A collector’s guide and FAQ written to answer what buyers actually ask (currency history, how to read a grading slab, telling originals from copies), so the site earns visits that no single listing could.

UX decisions

  • Arabic-first and RTL by default: not an English site translated, but a site whose native direction is right-to-left.
  • A reply-speed promise stated up front, and a floating WhatsApp button that follows the reader on every page, because on mobile the buy action should never be more than a thumb away.
  • Sold pieces stay browsable and honest: the page survives, the state flips, and the buy button does not ship on stock that is gone.
  • Every photo is the real item, shot as-is, with a note saying so: the trust signal that matters most in a market full of copies.
  • Descriptions derived from the seller’s own words with the price line, sign-off and keyword-stuffing removed; no invented rarity or provenance claims, since these are real goods sold to real buyers.

DecisionA sold piece keeps its page, its URL and its rankings. The state flips and the buy button disappears.

Analytics

Microsoft Clarity is live for session recordings and heatmaps, the qualitative half. The quantitative half is a PostHog event layer I wired through a first-party proxy on the site’s own domain, so a tracker blocker sees only same-origin traffic; it stays silent until the project key is set, and it no-ops safely without one. Its events are built around a placement property, so the useful question (which surface actually converts: the item page, a category, the guide or the floating button) can be answered rather than guessed at.

Worth a closer look

The part I like most is the attribution: a WhatsApp message arriving with TRH-0102 in it came from this site, and one without it did not. It needs nothing installed on the seller’s side, and it turns “is the site working?” into a question with an answer. The honest limit is that WhatsApp lets a sender edit the prefilled text, which is why the click is also recorded independently.

LimitWhatsApp lets a sender edit the prefilled text. Two records to reconcile, not one to trust.

Screens