$ cat ~/projects/lancangkuning.mdx

LancangKuning.com

lancangkuning.com

Rebuilt a slow Laravel 5 news portal with Go, Nuxt.js, MySQL, and Redis. Page load dropped from 45–60 seconds to under 1 second.

  • Go
  • Nuxt.js
  • MySQL
  • Redis

Context

LancangKuning.com was a regional Indonesian news portal that had grown for years on Laravel 5. Under load, the home page took 45–60 seconds to render. Publishing a breaking story could make the rest of the site unusable.

What I built

I replaced most of the backend and kept the existing content:

  • Go HTTP server with a thin, request-scoped middleware chain (auth, request ID, cache lookup, fallback to handler). The routing table is small — maybe a dozen handlers — but every one of them is cache-aware.
  • Normalized MySQL schema — the legacy database was denormalized to a degree that made every article list query a 7-table JOIN with a LIKE '%keyword%' on a TEXT column. The new schema has proper article/category/tag join tables and full-text indexes where they make sense.
  • Redis caching layered in two tiers: page-level (60s TTL on article pages) and fragment-level (longer TTL on the sidebar and footer). The invalidation story is "purge by article ID on publish" — no global flushes.
  • Nuxt.js frontend doing SSR so the first paint is server-rendered HTML, with hydration for the interactive bits.

Outcome

Page load went from 45–60 seconds to under 1 second. Editors could publish during traffic spikes without calling engineering. The new stack was also small enough to understand end to end, which made on-call easier.

What I'd do differently: I'd build the cache invalidation primitives first, then the cache layer, then the handlers. We did it handler-by-handler and the first version of the invalidation logic was a mess of "publish this, purge that" inline. Centralizing it in a small set of event-driven purges would have been cleaner.