Skip to content

Part 8.4 — Client-Driven Data with REST: Avoiding Over-Fetching

Site Console Site Console
3 min read Updated Feb 8, 2026 Backend Development 0 comments

Most discussions about over-fetching blame the backend.
That’s only half the story.

Over-fetching happens when clients don’t express intent clearly and servers respond with “everything, just in case.” In this post, you’ll flip the perspective and design React clients that pull only what they need—while keeping your REST APIs explicit, debuggable, and cache-friendly.


1. Client-Driven Does Not Mean Client-Controlled

Important distinction:

  • Client-driven → the client declares intent

  • Client-controlled → the client dictates structure

REST APIs should support the former, not the latter.

Your goal is to give the client choices, not power.


2. Start from the UI, Not the Endpoint

Before designing an endpoint, ask:

  • what does this screen render?

  • which fields are actually visible?

  • which interactions require more data?

Example: a users list page.

UI needs:

  • id

  • name

  • avatar

It does not need:

  • email

  • preferences

  • audit history

Design from the screen outward.


3. Use Different Endpoints (or Modes) for Different Screens

Avoid the “one endpoint for everything” mindset.

Good pattern:

GET /users           → list (summary)
GET /users/:id       → detail

Even better with explicit modes:

GET /users?view=summary
GET /users/42?view=detail

This makes intent visible in logs, caches, and metrics.


4. React Fetch Calls Should Be Honest

In React, avoid vague fetches like:

fetch('/api/users');

Prefer:

fetch('/api/users?view=summary');

This does two things:

  • documents intent at the call site

  • prevents accidental over-fetching

Your fetch URL is part of your API contract.


5. Co-locate Data Requirements with Components

Each component should make its data needs obvious.

function UsersList() {
  const [users, setUsers] = useState<UserSummary[]>([]);

  useEffect(() => {
    fetch('/api/users?view=summary')
      .then(res => res.json())
      .then(setUsers);
  }, []);
}

This keeps:

  • data scope local

  • refactors safe

  • dependencies explicit

Avoid global “load everything” patterns.


6. Progressive Data Loading Beats Big Payloads

Don’t fetch everything upfront “just in case.”

Instead:

  • load summaries first

  • fetch details on navigation or interaction

Example:

  • list page → summary

  • detail page → full data

This mirrors how users actually move through the app.


7. Client-Side Composition Is a Feature

GraphQL composes data on the server.
REST encourages composition on the client.

That’s not a flaw—it’s a trade-off.

React is good at:

  • composing UI

  • merging responses

  • handling async flows

Let the client assemble views from:

  • focused endpoints

  • predictable shapes


8. Avoid UI-Driven Endpoints

Bad REST design:

GET /dashboardData
GET /homePageStuff

These endpoints:

  • couple backend to UI

  • break when UI changes

  • don’t scale across clients

Design APIs around resources, not screens.


9. Use Query Parameters to Express Variants

Instead of new endpoints:

GET /posts?include=author
GET /posts?include=author,comments

React components choose what they need.

Server remains:

  • explicit

  • bounded

  • predictable

Validate these parameters strictly—never dynamically expose everything.


10. Don’t Mirror GraphQL on the Client

Avoid building:

  • dynamic query builders

  • field selection UIs

  • ad-hoc include logic

If the client starts “assembling queries,” you’re rebuilding GraphQL poorly.

REST works best with:

  • known options

  • limited combinations

  • strong contracts


11. Error Handling Must Match the Contract

If a client requests:

GET /users/42?view=detail

And the user doesn’t exist:

  • return a clear 404

  • with a predictable error shape

Do not:

  • return partial data

  • silently fall back to another view

Consistency beats convenience.


12. Measure Payloads, Not Just Requests

Over-fetching isn’t just about request count.

Watch:

  • response size

  • unused fields

  • parsing cost

Small, focused payloads improve:

  • performance

  • battery usage

  • perceived speed

Especially on mobile.


13. The Client’s Responsibility

A disciplined client:

  • requests only what it needs

  • documents intent in URLs

  • avoids speculative fetching

  • respects API contracts

When clients behave well, servers can stay simple.


14. Summary

You now know how to:

  • design React clients that drive data needs

  • avoid over-fetching without GraphQL

  • express intent clearly in fetch calls

  • load data progressively

  • keep APIs resource-oriented

  • preserve REST’s strengths

Related

Leave a comment

Sign in to leave a comment.

Comments