Volume 2

Q145: API Boundaries, Data Normalization and Contracts

Difficulty: SeniorFrequency: HighAnswer time: 10-14 minutes

What Interviewers Want To Evaluate

This question checks whether you can turn external data into safe frontend state. Interviewers want API contracts, normalization, DTOs, runtime validation, versioning, backward compatibility, error shape design, null handling, and frontend/backend collaboration.

Senior answers should sound like production architecture, not just fetch examples.

Short Interview Answer

An API boundary is where external data enters the frontend. Data should be validated, normalized, and converted into an application-friendly shape at that boundary. The UI should not depend directly on inconsistent backend payloads. Good contracts define field types, nullability, IDs, dates, errors, pagination, versioning, and compatibility rules. Runtime validation and normalization make the rest of the app simpler and safer.

Detailed Interview Answer

Frontend code often receives backend data shaped for transport, storage, or legacy compatibility.

The UI needs a stable application model.

API payload -> validate -> normalize -> app state -> UI

Skipping this step pushes inconsistency into every component.

API Contract

A good API contract defines:

field names
types
nullability
date formats
ID formats
pagination shape
error shape
authorization behavior
versioning behavior
backward compatibility

Ambiguity here becomes frontend complexity later.

DTO vs App Model

A DTO is a data transfer object. It is the shape crossing the network.

An app model is the shape your frontend wants to use internally.

function normalizeUser(dto) {
  return {
    id: String(dto.id),
    displayName: dto.name ?? "Unknown user",
    joinedAt: new Date(dto.joined_at),
    isActive: dto.status === "active",
  };
}

This decouples UI code from backend naming and legacy fields.

Nullability

Null handling must be explicit.

missing field
field present as null
empty string
zero
false
unknown value

These may have different product meanings.

Normalize them intentionally.

Date and ID Contracts

Dates and IDs need special care.

date-time instant: ISO string with timezone
date-only value: YYYY-MM-DD
large numeric ID: string
UUID: string
database ID above JS safe range: string

The frontend should not guess.

Error Shape

Error responses should be predictable.

{
  "code": "VALIDATION_FAILED",
  "message": "Validation failed",
  "fields": {
    "email": "Email is already used"
  }
}

Stable error codes are better than parsing human-readable messages.

Pagination and Lists

Pagination contracts should define:

items
cursor or page
hasNextPage
total count if available
sort order
filter echo
staleness rules

UI bugs often happen when list contracts are vague.

Versioning

APIs evolve.

Compatibility strategies include:

additive fields
versioned endpoints
feature flags
capability flags
deprecation windows
consumer-driven contract tests
schema validation

Frontend and backend teams should agree on change rules.

Runtime Validation

Validation can happen with schema libraries or hand-written guards.

The important part is boundary placement.

async function loadUser(id) {
  const response = await fetch(`/api/users/${id}`);
  const raw = await response.json();

  return normalizeUser(raw);
}

The rest of the app receives a known shape.

Normalized State

For complex data, normalize entities by ID.

const state = {
  usersById: {},
  userIds: [],
};

This simplifies updates, equality checks, caching, and relationship management.

Common Mistakes

A common mistake is passing raw API responses directly into deeply nested UI components.

Another mistake is parsing dates in every component instead of at the boundary.

Another mistake is treating backend error message text as logic.

Senior Trade-Offs

Normalization adds upfront code, but it reduces repeated defensive checks and UI inconsistency.

For tiny apps, direct payload usage may be fine. For senior systems, stable app models pay off quickly.

Code or Design Example

function normalizeInvoice(dto) {
  return {
    id: String(dto.id),
    customerId: String(dto.customer_id),
    totalCents: Number(dto.total_cents),
    currency: dto.currency ?? "USD",
    issuedAt: new Date(dto.issued_at),
    status: dto.status,
  };
}

Every conversion is visible and testable.

Debugging Workflow

When API data breaks UI:

capture raw response
compare with contract
check nullability and missing fields
check date and ID formats
check error response shape
add or update boundary normalization
add contract or schema tests
coordinate backend compatibility fix

Interview Framing

Explain the boundary pipeline: validate, normalize, and expose a stable app model. Then discuss contracts, errors, dates, IDs, versioning, and frontend/backend collaboration.

Revision Notes

API boundaries should absorb inconsistency so the rest of the frontend can stay predictable.

Key Takeaways

Senior frontend code treats API data as external input, not as trusted internal state.

Follow-Up Questions

  1. What is an API boundary?
  2. What is a DTO?
  3. Why normalize API data?
  4. What should an API contract define?
  5. How should nullability be handled?
  6. Why should large IDs often be strings?
  7. Why are stable error codes useful?
  8. What pagination fields matter?
  9. How do APIs evolve safely?
  10. Where should runtime validation happen?

How I Would Answer This In A Real Interview

I would say external API data should be validated and normalized at the boundary into a stable frontend model. Then I would discuss contract details like nullability, date formats, ID precision, errors, pagination, versioning, and tests that protect frontend/backend collaboration.