| title | Response Shape Viewer — Implementation Plan | ||
|---|---|---|---|
| description | User stories, architecture, and build phases for the derived fields-and-types view | ||
| audience |
|
||
| status | in-progress |
Wiki Home › Future Features › Plans
Plan for the Response Shape Viewer proposal: derive an endpoint's field/type shape client-side from a sample and show it as a table, with copy-as-TypeScript. Open choices live in the decision log. Built on the same deriveShape() utility and sample fetch as the Query Builder — whichever ships first creates the utility.
- Know before you fetch. As a learner opening an API's details page, I can see what fields its records have and their types before writing a line of code.
- Honest optionality. As a learner, I can see that only 12 of 50 sampled records have
deathdate— learning that real data is ragged and my code needs to handle missing fields. - Nested paths revealed. As a learner, I can see
name.firstas a path with typestring, which is exactly the string I'd use in a query filter. - Bridge to typed code. As a learner moving to TypeScript, I can copy a ready-made
interfacewith optional members where coverage is partial. - Never stale. As a maintainer, I never update a schema by hand — the view derives from live data, so dataset edits are reflected automatically.
A pure derivation over a fetched sample; no server changes, no stored schemas.
flowchart LR
E[Active endpoint] --> F[Sample fetch ?_limit=N<br/>TanStack Query, shared key]
F --> D[deriveShape\(\)]
D --> T[Fields table]
D --> TS[toTypeScript\(\)] --> C[Copy as TypeScript]
Lives in client/src/utils/ (with the Query Builder as its second consumer). Input: an array of records (or a single object for singular resources). Output: a tree of nodes — { path, types: string[], count, total, example, children }.
Inference rules, chosen so the honest answer is also the teachable one:
- Types per path are a set: a field that's sometimes a string and sometimes null reports
string | null. count / totalis coverage: how many sampled records carry the path at all.- Arrays recurse into element shapes; empty arrays report
unknown[]. - Objects recurse to a depth cap (4); beyond it, report
objectand stop. - Numbers aren't split into int/float; JSON has one number type and that's the lesson.
- Mixed structural types (sometimes object, sometimes string) report the union and don't recurse — rare, but real data does it.
A deliberately simple emitter, separate from derivation:
- One
interfaceper object node; the root name derived from the resource (PascalCase, naive singularization —characters→Character; imperfect is fine, it's editable text). - Coverage < 100% → optional member (
deathdate?: string). - Type sets → unions;
unknownwhere inference gave up. - No generics, no
Record<>, no cleverness — a teaching aid, not codegen. Output format scope is decision D2.
A fields table on the API details page: columns field path (indented by depth, collapsible), type, coverage (as 12/50 with a subtle bar), example (truncated). A "Copy as TypeScript" button on the header row. Placement relative to the Query Builder is D3. Loading and error states follow the existing data-fetching pattern.
Singular (object) resources render the same table from one record with coverage hidden.
| Phase | Scope | Done when |
|---|---|---|
1. deriveShape() |
Utility + edge-case handling (nulls, mixed types, empty arrays, depth cap) | Correct output for futurama (nested), csscolornames (flat), and a hand-built pathological sample |
| 2. Fields table | Table component, collapse/expand, coverage display, loading/error states | Renders every existing dataset without blowing up |
| 3. TypeScript emitter | toTypeScript() + copy button + toast |
Emitted interfaces compile under tsc --strict for 3 representative datasets |
| 4. Placement + polish | Placement per D3, empty states, CSS | User-story walkthrough passes; feature doc written in docs/features/ |
deriveShapeandtoTypeScriptare pure functions — the strongest candidates in the client for real unit tests (vitest; same call as in the Query Builder plan, phase 1 of whichever builds first).- Compile-check emitted interfaces by pasting into a scratch
.tsfile undertsc --strict— cheap and catches emitter bugs precisely. - Manual: verify coverage numbers against a dataset where you know the raggedness (e.g. count
deathdateoccurrences in futurama.json).
- Fetching beyond the sample for exact coverage (sample-size trade-off is D1).
- JSON Schema / Zod / other emitters unless D2 says otherwise.
- Persisting or diffing shapes over time.
- client/src/pages/APIDetails/APIDetails.tsx — host page
- client/src/components/Playground/JsonTree.tsx — rendering base to borrow from
client/src/utils/deriveShape.ts— the shared utility (to be created)
- Response Shape Viewer — Decisions
- Proposal · Roadmap
- Query Builder plan — co-owner of
deriveShape() - Endpoint JSON Format — why datasets are ragged