A Tour of the Codebase
The fastest way to learn a codebase isn't reading the directory listing — it's following one real request from a component all the way to the backend and back. That's what we'll do here. Along the way we'll pass through every directory you'll touch in your first month, in the order you'd actually encounter them.
Open the repo in your editor and follow along.
The journey of one request
Our specimen: the operational camera data that keeps every tile fresh — pan, tilt, zoom, latest image.
Stop 1 — a component asks. Somewhere in components/, CameraListComponent needs live data
for the tiles on screen. It doesn't fetch anything itself. It calls a hook:
const { data } = useCamerasAllOperational(pollingCameraIds);
Stop 2 — the query hook. Open queries/useCamerasAllOperational.ts. This folder holds one
hook per backend endpoint, each wrapping TanStack Query. The hook's job: define the cache key,
the refetch interval, and the actual fetch('/api/cameras-all-operational'). Notice the URL —
it's our own server, not the backend.
Stop 3 — the BFF route. Open pages/api/cameras-all-operational.ts. This is the other side
of that fetch: a Next.js API route running server-side. It validates the request, attaches the
access token the browser never sees, and forwards to the real .NET backend at BFF_BASE_URL.
Stop 4 — back up the chain. The backend responds, the route relays it, TanStack Query caches it, the component re-renders. Round trip complete.
That path — component → query hook → BFF route → backend — is the shape of nearly every
feature in this app. When you build a feature that needs data, you'll write stops 2 and 3
yourself: one file in queries/, one in pages/api/.
The map of the territory
Now that you've walked one path, the directory layout will actually mean something:
app/— pages and routing: the actual screens operators see.components/— the UI, organized smallest-to-largest:atoms/→molecules/→organisms/. A button is an atom; the camera tile grid is an organism.queries/— one TanStack Query hook per backend endpoint (stop 2 of the journey).pages/api/— the BFF routes (stop 3). Nothing else lives inpages/.stores/— client state: ~30 Zustand stores, one file per concern. This is a big enough topic to be its own chapter.hooks/— shared React hooks, including thefeatureMap/family that drives the map.util/,lib/,models/— pure helpers, server-side plumbing, and TypeScript models, respectively.
This app runs Next.js's App Router (app/) and Pages Router (pages/) at the same time,
deliberately: every real screen lives in app/, and pages/api/ holds only the BFF routes. It
looks like a half-finished migration. It isn't — the BFF routes live where they were built, and
moving them buys nothing. Don't "fix" this.
Your first change
Time to earn your keep. The team convention for branches and commits:
- Branch off the default branch:
feature/<ticket-number>_<short-description> - Commit and PR titles:
<ticket-number> - <description>
For a first ticket, the highest-value habit is this: before writing code, walk the journey. Find the component, find its query hook, find its route. Nine bugs out of ten reveal which of the four stops they live at before you've written a line.
Wrapping up
You've traced the app's fundamental path — component → query hook → BFF route → backend — and you know which directory owns each stop. Everything from here builds on that spine.
Next chapter: Core concepts, starting with the architecture picture that puts the BFF in context of the whole platform.