StatsHub Docs

web

The Next.js product site — the app replacing legacy, on port 3002.

apps/statshub-web is the StatsHub product site and the successor to legacy. It serves the same product surface over the same data, rebuilt on the current Next.js — reading it through api rather than through a database client of its own.

bun run dev:web        # http://localhost:3002
bun run build:web
Next.js 16App Router, standaloneReact 19TypeScriptTailwind CSS 4Go APIevery readSupabasesession and authSWRclient fetchingnuqsfilters in the URLChart.jsStripesubscriptionsSentryZod

What it does

Value bets

/value-bets, and value-bets-v2/[view] and -v3/[view] beside it. Three generations of the model run at once on purpose, so a change to it is judged against the board it replaces rather than against memory.

Prop boards

ProfHunter ranks player props against their own history; the prop screener filters them by league, market, odds and hit rate. 100 Club, anytime goalscorer, assists and super subs are the same board cut differently.

Lineups and matchday

Confirmed and predicted starting XIs for upcoming matches, with fouls alerts next to them.

Trends and outliers

Best recent hit-rate streaks over a 3-to-40 game window, and the players sitting furthest from their own baseline.

Entity pages

A fixture, team, player or referee, each at /<kind>/<slug>/<id>. The slug is decoration — only the trailing id resolves.

The assistant

A chat that answers over the same data, streaming from /api/agent. It is the one route handler in the app; everything else it needs is an API call.

Referees, the odds converter, the bet builder, favourites and the data visualiser round it out — one directory each under src/composed.

How it is built

Filters are parallel routes

@filters, @importantFilters and @rightSidebar are slots on the (dashboard) layout. None may hold a [...segments] catch-all: a catch-all matches every url, which makes Next treat every path as routed and takes the legacy rewrites down with it.

Legacy is handed over a board at a time

LEGACY_ROUTES in next.config.js lists what still belongs to legacy. Those rewrites are matched before this app's own routes, so removing an entry is the hand-over — and adding a page without removing one means the page is never reached.

No database client

src/lib/server-api.ts is the whole data layer: a typed fetch at the Go API with a revalidate on every call. There is no src/db and no connection string in this app.

Prerendering reads the sitemap

src/lib/static-params.ts takes generateStaticParams off the sitemaps the API already serves, rather than adding a second enumeration to keep in step. It caps at 250 paths a route and returns none when the API is unreachable, because docker build has no API on its network.

Filter state lives in the URL

nuqs, in about forty files. A board's state is its query string, so a filtered view is a link — and the top bar and the filter panel bind to the same key, which is what stops them disagreeing.

proxy.ts, not middleware.ts

Next 16's rename. src/proxy.ts refreshes the Supabase session on every request that is not a static asset; the matcher is where that exclusion list lives.

Layout

DirectoryWhat is in it
src/appApp Router routes — all of them; there is no Pages Router left
src/aiModel tooling: the chat agent and its evals
src/composedOne directory per board — value-bets, prophunter, lineups
src/server, src/libServer-only helpers, including server-api.ts
src/hooks, src/contextsClient-side state
src/componentsApp-local components; shared ones live in @statshub/ui-web

There is no src/pages/api

It was deleted: api is the API. Anything that looks like it needs a Next.js route handler here almost certainly belongs in the Go service.

Environment

Copy apps/statshub-web/.env.example to .env.local. The Supabase URL and anon key are the two it cannot serve a request without — missing them gives a 500 reading Your project's URL and API key are required, not a build failure.

NEXT_PUBLIC_* values are inlined at build time, so changing one means a rebuild, and every one of them has to be listed in turbo.json's build.env or Turbo will serve a stale build.

Scripts beyond the usual

bun run chat           # the AI chat agent, in the terminal
bun run chat:eval      # its eval suite
bun run test           # vitest

On this page