Data Nexus

Infrastructure · We run it ourselves

Next.js

A React framework that decides, per route, whether a page is rendered once at build time or computed on every request.

This site is Next.js 16.2.12 with React 19.2.4 and Tailwind v4, and the version is not decoration — /version names the exact build a reader is looking at. The interesting property is not that Next.js renders React on a server; it is that the rendering strategy is a per-route decision rather than a project-wide one, so a page of prose costs nothing to serve while a crawler census that recomputes hourly costs only what it actually computes. The price of that granularity is a framework that changes its own conventions faster than any other dependency here, and it is paid in reading time on every upgrade.

nextjs.org

01/Why this one

  1. 01

    A single-page app makes the first meaningful answer wait for a JavaScript bundle to download, parse and execute before any text exists in the document. For a practice whose pages are read by automated clients as often as by people, that is the wrong trade in the wrong direction: /crawler-census exists precisely because those clients are counted, and a large share of them do not run JavaScript at all. Static HTML per route removes the question rather than optimising it.


  2. 02

    The alternative to a framework is a hand-rolled server, and the honest cost of that is not the first thousand lines — it is routing, streaming, cache-control headers, asset hashing, image handling and content negotiation, each maintained forever by whoever wrote it. The parts specific to this site are written here: the Markdown twin at /services/<slug>/md is ours, and scripts/check-markdown.ts fails the build when negotiation and the route table disagree. The parts that are not specific to anyone were declined.


  3. 03

    Static generation by default inverts the usual failure mode. In a render-everything-per-request architecture a slow data source turns into a slow page; here a page that has no server work performs no server work, so a failing source degrades the pages that read it rather than the site. The routes that must be dynamic — the census aggregate, the build identifier — are dynamic because they were marked as such, one at a time, not because the framework defaulted them there.


  4. 04

    React 19 server components let data-fetching code sit in the same file as the markup that consumes it without shipping that code to the browser. This matters most on the boring pages: a service page that reads its own structured data and renders it adds nothing to the client bundle, which stays reserved for the few components that genuinely need motion or state.


  5. 05

    The build is a place to enforce invariants, and this framework gives a build that fails. scripts/check-schema.ts breaks the deploy on a dangling @id in structured data, scripts/check-knowledge.ts breaks it on a corpus invariant, scripts/check-markdown.ts breaks it when a Markdown twin goes missing. On a hand-rolled static pipeline these checks would be a cron job that emails someone; here a broken knowledge graph never reaches production because there is no production build to carry it.


  6. 06

    It is not the answer given to every problem. pakibus is a Vite build with three.js and a post-processing pipeline deployed to Cloudflare Workers, openclaw is a Docker-composed Node service running grammY and Baileys with no web front end at all, and mozg is a Turborepo with versioned Prisma migrations. Next.js is what gets reached for when the deliverable is a set of addressable pages with mixed staleness — DYD-main, status-kvo-app, rakairport and seahero are all that shape — and not when the deliverable is a render loop or a message consumer.


  7. 07

    Vercel's hosting platform and Next.js are governed by the same company, which is a real coupling rather than a rumour. It is survivable here because the exit is legible: the framework runs on Node behind any reverse proxy, and the parts of this site that would be painful to move are the parts written here — negotiation, the check scripts, the census aggregation — not the parts the framework supplies. Naming the lock-in and measuring it is more useful than pretending a hand-rolled server has none.

02/What you can check

An entry that cannot point at something you can open, run or read does not compile. That is a property of the type, not a promise in a paragraph.

Case record
Ninety-nine pages and five hundred and forty-nine prerendered routes for a lease-to-own operator, all built from a single fleet snapshot per deploy.Read the record

On this site
Names the exact build being served, so the claim of 16.2.12 is checkable from the site rather than from this page.Open it

On this site
The deliberately dynamic route: every automated client that fetched the site, counted by purpose, recomputed hourly, aggregates only. The counterexample to static-by-default on the same deployment.Open it

On this site
The Markdown twin of a page, served by content negotiation on the same route — representation decided per route, not per project.Open it

Run it yourself
curl -sSI -H 'Accept: text/markdown' https://datanexus.ae/services/platform-architecture → Response headers showing the negotiated Markdown representation — the same URL answering differently by Accept header, not a separate .md file.

Run it yourself
curl -sS https://datanexus.ae/ | grep -c '<h1' → At least one heading present in the raw HTML with no JavaScript executed — the property a single-page app cannot offer a client that does not run scripts.

In our code
scripts/check-schema.ts — Build gate that fails the deploy on a dangling @id in structured data; alongside scripts/check-markdown.ts and scripts/check-knowledge.ts it is why a broken graph cannot reach production.

In our code
pakibus/package.json — The counter-case in the same inventory: three.js with a post-processing pipeline and a frame monitor, built with Vite and shipped to Cloudflare Workers. Next.js was not the answer there.

Property of the thing
Next.js changes caching defaults and route conventions between major versions, and deprecates APIs that were current a year earlier — so an upgrade is a reading task before it is a code task. — Read node_modules/next/dist/docs/ in an installed 16.x tree; the deprecation notices name what moved, and comparing them with the same documents in a 15.x install shows how much.
03/Where it stops

Invariant

The framework moves faster than the code written against it. Caching defaults have changed meaning between major versions without the calling code changing, conventions have moved between routers, and the guidance shipped inside node_modules/next/dist/docs/ is more current than most published tutorials and most model training data — which means every upgrade starts with reading, not editing. On a site edited weekly that cost is absorbed. On a project built once, handed over, and touched twice a year by someone else, it is a liability: the client inherits a dependency that drifts out from under them, and there a plain static generator or a boring server-rendered stack is the more honest recommendation. Next.js also returns nothing when the deliverable is not a set of addressable pages — a render loop, a bot, a queue consumer — where it adds a build system and a routing model in exchange for no benefit at all.