DEV Community

linou518
linou518

Posted on

Product Search Went Blank — Fixing an API Response Structure Mismatch

Product Search Went Blank — Fixing an API Response Structure Mismatch

Real experience: 2026-03-21, debugging an ERP admin panel

What Happened

Clicking the "Product Search" button in our ERP admin panel caused the entire page to go white. The console showed Cannot read properties of undefined (reading 'map').

Quick assessment:

  • Build was passing fine
  • Other pages worked normally
  • The crash happened the instant the search dialog opened

Finding the Root Cause

First, I checked the SearchDialog component. It calls .map() on the API response to render a product list, assuming that response.data is an array.

Then I hit the actual API endpoint and looked at the raw response:

{
  "code": 1,
  "data": {
    "list": [...],
    "total": 42
  }
}
Enter fullscreen mode Exit fullscreen mode

Here's the catch. The ERP's API client (request()) detects code === 1 as "success" and automatically unwraps the data field before returning it. So what the caller actually receives is:

{
  "list": [...],
  "total": 42
}
Enter fullscreen mode Exit fullscreen mode

SearchDialog tries to access response.data, but the received object has no data property. Hence undefined.map() — crash.

The Fix

Changing the API client's unwrapping behavior was too risky — no telling what else it might break. Instead, I added a getProducts() function to erpCompat.ts, a compatibility layer that normalizes the response:

export async function getProducts(params: ProductSearchParams) {
  const result = await request('/erp/products', params);

  // Normalize when API returns unwrapped { list, total }
  if (result && Array.isArray(result.list)) {
    return {
      data: result.list,
      total: result.total ?? 0,
    };
  }

  // Already in { data: [...] } format — pass through
  return result;
}
Enter fullscreen mode Exit fullscreen mode

SearchDialog just calls getProducts(). If the API's internal behavior changes, normalization is handled in one place.

Lessons Learned

API response unwrapping is not "transparent." If you don't realize that request() is silently stripping the data wrapper, the types at the call site will be "shifted" from reality. Even with TypeScript, this kind of bug lurks silently wherever types fall back to any.

Takeaways:

  1. Document the API client's unwrapping behavior (or express it through types)
  2. Always check actual response values (don't blindly trust type inference)
  3. Normalize via a compatibility layer (don't let components touch raw API responses directly)

This bug only surfaced after deployment, but thanks to the compatibility layer design, the fix was minimal. A thin wrapper function really does pay for itself.

Top comments (0)