Search and state

Search, filter, sort and page through this site’s own published work records. The requests are real; the slow and failing modes are simulated in the browser and say so.

This explorer needs JavaScript for search, filtering and paging. The full list of published work is below.

Loading results…

Page 1 of 1

How it works

The endpoint

Searches call GET /api/lab/projects, a read-only Next.js route handler over the same published records this site renders. There is no database, no authentication and no API key, and nothing about a visitor is stored.

q
Trimmed text, 100 characters maximum. Matched against title, summary, role and technologies.
category
One of the published category values, or all.
sort
title-asc or title-desc.
page
A positive integer. The page size is fixed at three and is not client-supplied.

The response carries the matching result summaries, the total count, the current page and the total page count. Validation rejects rather than silently corrects: anything outside that contract returns a 400 naming the offending parameter. A valid page number past the end of the results is not an error, and returns an empty result list.

Only a fixed summary shape leaves the server — slug, title, category, summary, role, period, technologies and the entry’s own path. The longer authoring notes on each record are never serialized into a response.

Request handling in the browser

Typing is debounced by 250 milliseconds, so a search phrase costs one request rather than one per keystroke. Each new request aborts the previous one through an AbortController, and also gets a sequence number: when a response arrives, it is discarded unless its number is still the newest. Aborting alone is not enough, because a response can already be in flight when the abort lands — the sequence check is what guarantees a slow older reply can never overwrite a newer result. Changing the query, the category or the sort resets the page number, since a page three of the previous result set means nothing in the new one.

While a request is in flight the previous results stay on screen, dimmed, with the status line announcing the update through an aria-live region. Loading, empty, error and success are four distinct states rather than one spinner.

The demonstration modes are not the server

The slow and failing modes exist entirely in the browser. The slow mode adds a delay in front of a normal request; the endpoint answers at its usual speed and is never asked to sleep. The failing mode produces its error locally without any network call at all — the endpoint is not made to crash or do expensive work. Both label themselves on screen, so the failure is never mistaken for a real outage, and Retry on the same query is allowed through so the recovery path is genuinely reachable.

The slow mode is also the easiest way to see the stale-response protection: switch it on, type a search, then immediately change it. The older, slower response is discarded rather than replacing what you asked for last.

Without JavaScript

The controls are hidden rather than left inert. The full list of published work is rendered on the server instead, with each entry linked to its own page. Both the interactive interface and that list ship in the same HTML, so switching between them never moves the page.

Other experiment: 3D project ring