🗳️ Thairath Bangkok Governor Election 2026 – Live Results Platform
Built the
data, backend API and CMSbehind Thairath's coverage of the 2026 Bangkok governor and city council (ส.ก.) election, plus the frontend API integration, the home page motion and the news page with its infinite feed.
📚 Overview
The site followed the race from candidate registration to the final count: live governor results, a hex map of all 50 districts, council results for every district, candidate profiles and comparisons, a public poll on Bangkok's problems, and a news hub.
Behind it is one API that gathers every source (a third-party results partner, Thairath's own poll and news services, and the master data I prepared) and serves it cached, with a CMS so editors decide what is published and when.
Readers can:
Follow the governor count live, with every candidate's votes and share
Explore the 50-district hex map and each district's governor and council results
Browse and compare governor candidates and their policies
See how Bangkok voted in 2022 next to 2026
Read the poll on the city's most urgent problems
Scroll an endless feed of news, videos and shorts

🚀 Core Architecture
🔄 Data Pipeline
Results partner API → scheduled sync jobs (Agenda on PostgreSQL) → raw tables
A publish switch in the CMS copies raw data into the published tables, so nothing reaches readers before it is checked
Two result sources from the partner (realtime and final), chosen by the CMS (auto, realtime or final) or per request
Master data (50 districts, 276 candidates, 10 parties), the 2022 results (412 candidates, 1,994 score rows) and 7,461 sub-district search entries converted by scripts into static JSON
Sync jobs tightened from every 5 minutes to every 30 seconds on election day
⚙️ API and Caching
Express 5, Drizzle ORM and PostgreSQL, deployed on Kubernetes
A Redis response cache on every public route, keyed by path and sorted query, gzip-compressed above a size threshold, with an X-Cache header (HIT, MISS, REFRESH or BYPASS)
TTLs per kind of data: 60 seconds for the livestream, 10 minutes for feeds and timelines, 1 day for master and 2022 data
A refresh query flag and a CMS
Clear Cachebutton to push a correction out at once
Live result responses carry the CMS settings, so the site knows the current source and how often to refresh
OpenAPI docs for every route
🛡️ Partner Resilience
Two partner API keys: on HTTP 429 the client switches keys, then waits for the rate-limit reset before switching back
Every partner request retries twice with a 5-second timeout
🚀 Key Features
🧭 CMS for Editors
Email one-time-code login, with the session in a cookie
Candidate profiles and policies with image upload, the election timeline and the livestream link
Publishing controls: election started, publish partner data, data source, and live refresh on or off with its interval
One-click cache clear
📰 Infinite News Feed
The first 18 items are rendered on the server with loading skeletons, then a virtualised masonry grid (masonic) loads the next page as the reader nears the end
Cursor-based pagination for each tab (all, news, videos, shorts), each with its own state and a lazy first load
An in-flight guard so a tab never requests the same page twice
A tall and short rhythm in the grid, with measured frame sizes for videos and shorts

✨ Motion
A splash screen with a 0 to 100 counter and four columns sliding away
Parallax on the home cover (desktop only)
An election timeline with a tuk-tuk that drives to the current milestone
Top-three result counters that count up to the latest totals
🛠️ Tech Stack
- Frontend
: Next.js 16, React 19, Emotion, Framer Motion, masonic
- Backend
: Node.js, Express 5, Drizzle ORM
- Data
: PostgreSQL, Redis (cache and pub/sub), Agenda jobs
- CMS
: Vite, React, shadcn/ui
- Infrastructure
: Docker, Kubernetes, Jenkins
👨💻 Responsibilities
Owned all the data: static master data, the 2022 results, candidate and party images, live results from the partner, and Thairath's own poll and news feeds
Designed and built the backend API (107 of its 143 commits)
Built the partner integration, the sync jobs, the raw and published tables, and source switching
Built the Redis cache layer and the cache-clear tooling
Set up the CMS and built its config and cache-clear modules and cookie login
Integrated the frontend with the API: feeds and timelines, one client that switches between server and browser endpoints, and failed calls that never break the page
Built the news page and its infinite feed
Built the home page motion: splash screen, parallax, timeline and result counters
Ran the election-day operation: sync intervals, source switches and cache clears
🎯 Impact
- 2.37M+ page views from 91,800+ active users
(Google Analytics; the real count is likely higher)
- 16.5 views per session
and
1m 30saverage engagement per session
The district council results alone drew
1.43M views, all served from the cached API
Editors controlled every publish, and a correction reached readers in one click instead of waiting for caches to expire
Delivered the data, API, CMS and frontend integration in about eight weeks, from 5 May to election night on 28 June 2026



