Arena Academy is the site people actually look at: hero pages with statistics, builds, counters and trends, plus rankings, community ratings, equipment and emblem references, patch versions, and a signed-in account view. It stores nothing of its own — every figure on every page is fetched from the Rone Arena API and cached on the way through.
Nineteen routes, no database. The site is a rendering layer over one API, which makes every interesting decision a fetching decision: where requests are made, what is cached and for how long, and what happens when the API is the thing that is down.
Reference material — roles, equipment, spells, emblems, ranks, guides, patch versions — renders on the server and revalidates on a timer. Anything tied to a signed-in player renders in the browser against a session held locally.
Every resource module funnels through a single request function, so exactly one place in the codebase knows the base URL, how query parameters serialise, what the error shape is, and how to unwrap the response envelope. No page or component calls fetch against the API directly. When the API turned out to have a second envelope shape for two endpoints, that was one branch in one function rather than a search across the tree.
Caching is layered by audience. Server-rendered requests use the framework data cache with a five-minute revalidation window. Unauthenticated GETs made in the browser also hit a small in-memory map with the same lifetime, which absorbs the repeat requests a reader generates by moving between tabs on a hero page. Authenticated calls skip both, unconditionally — a cache keyed on URL alone is a way to serve one player another player’s profile.
Match history pages on a cursor that is a 64-bit id. The API serialises it as a string for a reason: it exceeds the largest integer JavaScript holds exactly, and passing it through Number silently corrupts it. Pagination then works for the first page and quietly fails afterwards. The cursor is kept a string end to end.
Skill and equipment descriptions arrive carrying the game’s own inline markup — colour spans, literal newlines, and unresolved template placeholders in angle brackets that are not markup at all. Rendering that raw would be an injection vector; escaping it wholesale would show readers angle brackets. The string is escaped completely first, then exactly two patterns are restored, which leaves the placeholders as inert text.
Theme is applied by an inline script before first paint, and it resolves the system preference to a concrete light or dark value rather than leaving an ambiguous third state for the toggle to represent. Sessions and language preferences written under the pre-rebrand storage keys are adopted once and the old keys removed, so the rename signed nobody out and reset nobody’s language.
It runs on Cloudflare Workers through the OpenNext adapter, with the API base URL fixed at build time so a stale environment variable cannot point a deployed bundle somewhere unintended.