Arabic, right to left, and always breaking.
Kul al-Arab covers local news town by town, politics, sport and culture for Arabic-speaking readers. The newsroom works on a long-running PHP site and a news table large enough that changing its schema is expensive.
A table too big to change
Years of stories in one MySQL table. New ideas like flags, authors and homepage slots couldn't mean altering it.
Cached pages, breaking news
Pages are served from the CDN edge for speed, so a breaking story or a homepage change has to clear the right pages without flushing the site.
One story, many channels
The same story goes to the website, to readers' phones as a push, and to Facebook, Instagram, TikTok, Telegram and WhatsApp, each in its own format.
A homepage curated slot by slot
Editors place the main stories by hand in numbered slots, and a change has to reach readers at once.
Video at volume
Large uploads had to get past request size limits, and every video needed renditions that play on any connection.
Elections as data
Election coverage needs lists, candidates and pollsters' seat estimates as structured data, not as articles.
One API between the newsroom and everywhere a story goes.
The CMS talks only to the API. The API writes to the existing database, keeps its own Redis cache, and drives the site's cache, the push to the apps and the social networks.
Newsroom CMS · React · Arabic · right to left
Video uploads · 8 MB chunks · ffmpeg ladder
Elections admin · lists · candidates · seat estimates
CMS API · NestJS and TypeORM, with a versioned Redis cache.
MySQL news database · existing · shared with the PHP site
Site origin and Bunny CDN · origin refresh, then exact purge
Pushwoosh · push to the iOS and Android apps
Social networks · Facebook · Instagram · TikTok · Telegram · WhatsApp
Video: renditions from 240p to 1080p, served as HLS by nginx-vod · Comments, reactions, views and poll votes: Supabase
Four decisions that shaped the CMS.
Leave the news table alone
The new CMS and API work on the existing database without migrating it. Flags, authors and homepage slots live in side tables, so the very large news table is never altered.
Purge exactly what a story touches
Purging is part of publishing. The API asks the site's origin to refresh, takes back the list of URLs, and purges each one on Bunny CDN: the article, its page, its town feed and its category feeds. Changing a homepage slot refreshes the homepage by itself.
Publish everywhere from one screen
Push through Pushwoosh with a phone preview, posts per network from branded templates, and story images rendered at 1080×1920 from the article's own data, all from the article screen.
Video in renditions
Uploads go in 8 MB chunks that halve when the server refuses the size. An ffmpeg ladder makes 1080p down to 240p, nginx-vod serves them as HLS, and the CMS checks each video's status every two minutes.
What else editors get, in Arabic, right to left.
Homepage curation
Numbered slots with drag and drop and an article picker. Empty slots get a soft warning, and every position change is logged.
The article editor
Drafts, a live list, restorable deletes, tags, galleries with drag-sort, a 15:10 cover crop and a log of every change.
Elections data
Eight tables for election years, candidate lists, a shared roster of people, pollsters' seat estimates, a timeline and an FAQ, feeding the site's countdown and charts.
Search in the CMS
By article id or by words from the title. Every word is required and prefix-matched in a MySQL full-text index, which helps with short Arabic and Latin terms.
Fast lists on a large table
Redis caches editors' lists and dashboard figures. A content version goes up on every edit, so stale lists drop out at once.
Roles and sessions
Editors, moderators, admins and superadmins with per-permission guards, and refresh tokens that rotate and detect reuse.