Scenario

Build a small dashboard combining data from at least two independent sources (two different public APIs, or one API plus a local JSON file standing in for a second) — a weather-plus-air-quality view, a repo-stats-plus-issues view, whatever combination is available and interesting. The dashboard’s filter/view controls should live in the URL, so a specific view is shareable and survives a reload.

Requirements

Core

  • At least two independently fetched data sources render into the same view, each with its own loading/empty/error state (recall Asynchronous Basics and Data Fetching and Asynchronous JavaScript in Depth) — one source failing must not blank out the other’s already-loaded data.
  • Changing a filter or view control updates the URL (via the History API) without a full page reload, and loading the dashboard directly from a URL with parameters restores that exact view.
  • Switching filters while a previous request for the same data is still in flight cancels the stale request (AbortController) or otherwise ignores its result if it arrives late — no race-condition overwrite of newer data with a stale response.
  • Repeated identical requests within a short window are served from a simple in-memory cache instead of re-fetching.
  • Data is presented as an accessible table (or a chart with an equivalent accessible data table alternative) — never a chart with no non-visual way to access the same data.

Stretch

  • A “last updated” timestamp per data source, with a manual refresh control.
  • Graceful degradation when one source is slow: show the fast source’s data immediately, the slow source’s once it arrives, rather than waiting for both.
  • Debounce a text-filter control so it doesn’t re-fetch on every keystroke.

Reflect

  • Where did the race-condition risk actually live in your implementation — which specific state update needed the “is this still the most recent request” guard?
  • If a third data source were added, what in your current design would need to change versus what would just work?

Suggested milestones

  1. Render one data source with proper loading/empty/error states, no URL syncing yet.
  2. Add the second data source, independently loaded and independently error-handled.
  3. Wire filter controls to the URL (pushState) and restore state from it on load.
  4. Add cancellation/staleness handling for rapid filter changes.
  5. Add the accessible table/chart-alternative presentation and a caching layer.
  6. Write at least one integration test exercising a filter change end to end.

Acceptance checks

  • Loading the dashboard with URL parameters already set (a bookmarked/shared link) restores that exact filtered view on first load.
  • Rapidly changing a filter several times in quick succession settles on the result matching the last selected filter, never an earlier one arriving late.
  • One data source returning an error still leaves the other source’s data visible and usable.
  • All data is reachable via a screen reader — a chart alone, with no accessible table alternative, fails this check.