Build and Optimize a React Interface Against an Undocumented API
StripeFrontend Engineer · OnsiteLast updated
Tests your ability to inspect an opaque or changing backend endpoint, build a resilient React component with typed normalization and client-side caching, and handle edge cases gracefully.
Scenario
You are tasked with building a DisputeReviewPanel component in React that fetches and displays payment disputes from an undocumented internal endpoint: GET /api/v1/disputes.
Because the backend schema is currently evolving without formal documentation, you must inspect the actual response payloads, defensively normalize the data into a strict client-side model, implement client-side caching to prevent redundant network requests when switching filters, and handle race conditions when search inputs change rapidly.
Requirements
-
Data Discovery & Normalization: The API returns an array under
data, but individual dispute items use inconsistent property names and formatting:- Identifier may be
idordispute_id(string). - Amount might be integer cents (
amount: 2500), a float (amount_dollars: 25.0), or a string ("$25.00"). Normalize all amounts to integer cents. - Status is a string that maps to one of three normalized states:
'needs_response','under_review', or'closed'. Unrecognized statuses should default to'needs_response'. - Creation timestamp may be Unix seconds (
created) or ISO 8601 string (created_at). Normalize to standard Unix epoch milliseconds.
- Identifier may be
-
Component Interface & State:
- Implement
DisputeReviewPanel({ fetchDisputes, pageSize = 10 }: DisputeReviewPanelProps). - Support filtering by normalized status:
'all' | 'needs_response' | 'under_review' | 'closed'. - Implement search by dispute identifier or reason.
- Implement
-
Caching & Concurrency:
- Maintain an in-memory client cache keyed by query parameters (
status,search,page) with a 60-second time-to-live (TTL). - Ensure in-flight requests are aborted or ignored if the filter/query changes before the response arrives (prevent race conditions / stale overwrites).
- Provide standard loading (
Loading...), error (Retry), and empty states (No disputes found).
- Maintain an in-memory client cache keyed by query parameters (
Example Usage & Normalization
// Raw API payload snippet:
{
"data": [
{
"dispute_id": "dp_101",
"amount": 4200,
"status": "warning_needs_response",
"created": 1700000000
},
{
"id": "dp_102",
"amount_dollars": 15.5,
"status": "WON",
"created_at": "2023-11-14T22:13:20Z"
}
],
"has_more": false
}
// Expected normalized model:
[
{
id: "dp_101",
amountCents: 4200,
status: "needs_response",
createdAtMs: 1700000000000
},
{
id: "dp_102",
amountCents: 1550,
status: "closed",
createdAtMs: 1700000000000
}
]
Constraints & Edge Cases
- Do not mutate the raw API response object directly.
- Handle floating-point arithmetic errors when converting dollar floats to cents (e.g.,
Math.round(val * 100)). - If the network fails, allow the user to click
Retrywithout discarding existing input values. - Cache keys must be deterministic regardless of parameter order.
Hints
Hint 1
Write an explicit parser/adapter function with runtime checks (such as checking `typeof` or using a schema validator) before touching React state.
Hint 2
Use an `AbortController` inside a `useEffect` hook to cancel inflight fetch requests when filter dependencies update.
Hint 3
Structure your in-memory cache as a Map or plain object mapping a normalized string key like `${status}:${query}:${page}` to `{ data, timestamp }`.
Follow-up questions
- How would you integrate an automated schema validation library like Zod to alert your team when the undocumented API introduces breaking changes?
- If users can resolve disputes directly in this UI, how would you handle cache invalidation and optimistic updates?
- How would you adapt the caching layer to persist across browser page reloads without risking stale financial data?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.