---
term: 'Overfetching & Underfetching'
seoTitle: 'Overfetching & Underfetching: Causes, Costs, and Fixes'
headline: 'What are Overfetching & Underfetching?'
slug: overfetching-underfetching
category: api-realtime
shortDefinition: 'Overfetching is an API problem where responses carry more data than the client needs; underfetching forces extra requests to get enough.'
relatedTerms:
  - api-payload-optimization
  - n-plus-one-query-problem
  - graphql-vs-rest
  - rest-api
contrastsWith:
  - graphql-vs-rest
aboutTerms:
  - 'Overfetching'
  - 'Underfetching'
faq:
  - question: 'What is overfetching?'
    answer: 'An API returns more data than the client needs for the task at hand — a profile screen that renders three fields receives forty. The waste is paid four times: server serialization, network transfer, client parsing, and, on mobile, battery and metered data. It can also expose fields no client should see.'
  - question: 'What is underfetching?'
    answer: 'A single endpoint does not return enough data to render the screen, so the client makes additional requests to assemble it. Each extra call is a full network round trip; when the calls are sequential — fetch the list, then fetch details per item — latency compounds into the N+1 request problem.'
  - question: 'What is the difference between overfetching and underfetching?'
    answer: 'Direction. Overfetching means each response carries too much — the cost is bytes; underfetching means each response carries too little — the cost is round trips. Both grow from the same root: fixed response shapes designed once, consumed by screens with different needs. Many APIs manage both at the same time on the same screen.'
  - question: 'Does GraphQL solve overfetching and underfetching?'
    answer: 'Largely, at the HTTP layer: selection sets fetch only requested fields and nested queries gather related data in one request. But it is not automatic — clients that request generous field sets recreate overfetching, and naive resolvers recreate underfetching against the database as resolver-level N+1, which batching loaders exist to fix.'
  - question: 'How do you avoid overfetching in a REST API?'
    answer: 'Sparse fieldsets are the direct fix: a fields parameter (or select() in SDK query builders) that projects only the columns the screen renders. Pagination bounds list size, purpose-built endpoints match responses to real screens, and compression shrinks whatever remains — though compressing bloat is a mitigation, not a cure.'
  - question: 'How do you fix underfetching without switching to GraphQL?'
    answer: 'Expansion parameters — include or expand — that embed related objects in one response; compound documents that ship a resource with its associations; composite endpoints that aggregate one screen''s needs server-side; and, architecturally, a backend-for-frontend layer that does the assembly close to the data instead of across a mobile network.'
  - question: 'How does the N+1 problem relate to underfetching?'
    answer: 'N+1 is underfetching at scale: one request for a list of N items, then N follow-up requests for the details of each. The same shape recurs at every layer — HTTP clients against REST endpoints, GraphQL resolvers against the database, ORMs lazy-loading relations — and the fix is always the same idea: batch the N into one.'
  - question: 'Why is overfetching a security risk?'
    answer: 'Fields a screen never renders still cross the network — and anyone can open developer tools and read them. Internal flags, email addresses, and cost data leak this way; security taxonomies classify it as excessive data exposure, and the principle of least privilege applies to response bodies just as it does to permissions.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'GraphQL specification — selection sets'
    url: 'https://spec.graphql.org/October2021/#sec-Selection-Sets'
  - name: 'JSON:API specification — sparse fieldsets'
    url: 'https://jsonapi.org/format/#fetching-sparse-fieldsets'
  - name: 'OWASP API Security Top 10'
    url: 'https://owasp.org/API-Security/'
  - name: 'Backends For Frontends pattern — Sam Newman'
    url: 'https://samnewman.io/patterns/architectural/bff/'
cta:
  title: 'Fetch exactly what the screen needs'
  text: 'Back4app queries take select() and include() on every SDK — sparse fieldsets and one-round-trip relations without designing a single endpoint — plus a GraphQL API when clients want to shape responses themselves.'
  linkText: 'Start building free'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-07-24'
translationKey: overfetching-underfetching
---

**Overfetching is an API problem where responses carry more data than the client needs; underfetching forces extra requests to get enough.** They are the twin failure modes of fixed response shapes — one wastes bytes, the other wastes round trips — and most APIs commit both on the same screen: each response too fat, and too many of them.

## Key takeaways

| Question | Answer |
| --- | --- |
| Overfetching | Too much per response — bandwidth, parsing, battery, exposure |
| Underfetching | Too little per response — extra round trips, waterfalls, N+1 |
| The root cause | Fixed endpoint shapes meeting screens with different needs |
| REST fixes | Sparse fieldsets · include/expand params · pagination · composite endpoints |
| GraphQL's answer | Selection sets — with honest caveats at the resolver layer |

## One screen, three ways to fetch it

A post list that renders each title with its author's name:

```text
Overfetching                          Underfetching
GET /posts                            GET /posts            (author IDs only)
→ 20 posts × 40 fields                GET /users/11  ┐
→ ~160 KB shipped,                    GET /users/12  │  20 more calls —
  ~6 KB rendered (96% waste)          …              │  the N+1 waterfall
                                      GET /users/30  ┘

The shaped query
GET /posts?fields=title,summary,author&include=author&limit=20
→ 20 posts × 3 fields + their authors — one round trip, ~7 KB
```

The same shaped query as SDK code — projection and relation in one request:

**JavaScript:**

```javascript
// JavaScript / Node.js — Back4app JS SDK
// One shaped query: no overfetch, no underfetch
const query = new Parse.Query('Post');
query.select('title', 'summary', 'author'); // only what the screen renders
query.include('author');                    // related object, same response
query.limit(20);                            // bounded page
const posts = await query.find();
// 1 round trip — not 1 list call + 20 author calls (N+1)
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// One shaped query: no overfetch, no underfetch
final query = QueryBuilder<ParseObject>(ParseObject('Post'))
  ..keysToReturn(['title', 'summary', 'author']) // only what the screen renders
  ..includeObject(['author'])                    // related object, same response
  ..setLimit(20);                                // bounded page
final response = await query.query();
// 1 round trip — not 1 list call + 20 author calls (N+1)
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// One shaped query: no overfetch, no underfetch
let query = Post.query()
  .select("title", "summary", "author") // only what the screen renders
  .include("author")                    // related object, same response
  .limit(20)                            // bounded page
query.find { result in
  // 1 round trip — not 1 list call + 20 author calls (N+1)
  if case .success(let posts) = result { render(posts) }
}
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// One shaped query: no overfetch, no underfetch
val query = ParseQuery.getQuery<ParseObject>("Post")
query.selectKeys(listOf("title", "summary", "author")) // only what the screen renders
query.include("author")                                // related object, same response
query.limit = 20                                       // bounded page
query.findInBackground { posts, e -> if (e == null) render(posts) }
// 1 round trip — not 1 list call + 20 author calls (N+1)
```

## What is overfetching?

Overfetching is the API-layer version of `SELECT *`: the endpoint returns its full fixed representation regardless of what the caller renders. The costs stack in layers. The server serializes fields nobody reads; the network carries them — which is where mobile suffers, since transfer time scales with bytes over constrained bandwidth and every needless kilobyte spends metered data and radio battery; then the client parses the whole thing, because decompression happens before rendering and a bloated response is bloated parse work even when compression hid it on the wire.

The quieter cost is exposure. A response field the UI never shows is still one developer-tools click from being read — internal flags, other users' email addresses, margin data. The [OWASP API Security Top 10](https://owasp.org/API-Security/) tracks this as excessive data exposure (broken object property level authorization): least privilege applies to response bodies, and a field that no client should see should never be serialized in the first place.

## What is underfetching?

Underfetching is the opposite deficiency: the endpoint's fixed shape carries too little, so the client becomes an integrator — fetch the list, then fetch each item's author, then maybe each author's avatar. Every extra call is a full round trip, and round trips are the currency mobile networks are poorest in: at a realistic 100 ms per request, a 20-item list resolved sequentially spends two seconds *on latency alone*, before a byte of payload math.

At scale this waterfall has a name — the [N+1 request problem](/glossary/n-plus-one-query-problem/): one call for N items, N calls for their details. The shape is fractal; it recurs wherever a fixed interface meets relational data — HTTP clients against REST endpoints, GraphQL resolvers against the database, ORMs lazy-loading their way through a loop — and the cure is always some form of batching the N into one.

```mermaid
flowchart LR
  accTitle: Underfetching waterfall versus one shaped query
  accDescr: With underfetching, the client makes one request for a list and then one sequential request per item, multiplying round-trip latency. A shaped query returns the list with its related data in a single round trip.
  subgraph W["Underfetched: 1 + N round trips"]
    L["GET /posts"] --> U1["GET /users/11"] --> U2["GET /users/12"] --> U3["… × 20"]
  end
  subgraph S["Shaped: 1 round trip"]
    Q["GET /posts?fields=…&include=author"]
  end
```

## Overfetching vs. underfetching

| | Overfetching | Underfetching |
| --- | --- | --- |
| Symptom | Responses full of unrendered fields | Screens assembled from many calls |
| Unit of waste | Bytes (and parse time) | Round trips (and latency) |
| Worst on | Metered, slow, battery-bound networks | High-latency networks — waterfalls compound |
| Detection | Compare fields returned vs. fields rendered | Count requests per screen in the network tab |
| Direct fix | Sparse fieldsets / projection | Expansion params, composite endpoints |
| Escalation | Excessive data exposure (security) | N+1 request storms (scale) |

Diagnosis is mercifully mechanical, and no ranking explainer says so: open the network tab on one screen. Many requests for one view is underfetching; large responses whose fields you can't find in the UI is overfetching. Endpoint analytics generalize the audit — p95 payload size per endpoint, requests per session per screen.

## Fixing both without leaving REST

The GraphQL migration is not the first resort; mature REST conventions cover most of the gap:

- **Sparse fieldsets** — a `fields` parameter that projects the representation: standardized as [JSON:API sparse fieldsets](https://jsonapi.org/format/#fetching-sparse-fieldsets), mirrored by `$select`-style query options and SDK `select()` builders. The overfetching fix at the source.
- **Expansion parameters** — `include=author,comments` embeds related resources in the same response (compound documents), converting an N+1 waterfall into one request. The underfetching fix at the source.
- **Pagination** — bounds the list dimension of overfetching; unbounded collections are payload bugs that grow with adoption.
- **Purpose-built and composite endpoints** — when one screen always needs the same aggregate, give it an endpoint that returns exactly that aggregate, assembled server-side where latency between services is microseconds, not mobile round trips.
- **A backend-for-frontend** — the architectural version of the same move: a thin per-client layer that speaks generous internal APIs and serves each frontend exactly its shape ([Sam Newman's BFF pattern](https://samnewman.io/patterns/architectural/bff/)).
- **Compression** — honest last place: it shrinks the wire, not the waste; parse cost and exposure survive it intact.

## Does GraphQL solve it?

Mostly — and the "mostly" is worth knowing. [Selection sets](https://spec.graphql.org/October2021/#sec-Selection-Sets) make the client's field list *the request itself*, which retires classic overfetching, and nested queries assemble related data in one round trip, which retires classic underfetching. That is exactly why the [GraphQL vs. REST](/glossary/graphql-vs-rest/) debate starts with these two words.

The caveats live one layer down. Clients that copy-paste generous queries overfetch by habit — nothing enforces that a query matches what a component renders unless the team adopts per-component fragments. And a naive resolver chain *underfetches against the database*: a query for 20 posts with authors becomes 1 + 20 database reads unless resolvers batch through a loading layer — the same N+1, relocated. GraphQL moves the problem to a layer you control, which is genuine progress; it does not delete it.

## Common use cases

Where the two problems (and their fixes) show up first:

- **Mobile list screens** — the canonical overfetch: full rows shipped to render three fields per cell.
- **Detail screens with relations** — post + author + comments: underfetch waterfalls unless expanded or composite.
- **Slow-network markets** — both problems taxed at the highest rate; shaped queries as accessibility.
- **Dashboards** — aggregate screens that either overfetch raw rows or underfetch across five services; BFF territory.
- **Public APIs with diverse consumers** — one fixed shape cannot fit a watch face and an admin console; projection and expansion parameters let each caller tune.

## Which problem do you have? A decision matrix

| Network-tab evidence | Diagnosis | First fix |
| --- | --- | --- |
| One request, big response, few fields rendered | Overfetching | Sparse fieldsets / `select()` |
| Many sequential requests per screen | Underfetching | `include` / expansion params |
| Requests scale with list length | N+1 | Batch: expansion or composite endpoint |
| Both large and many | Both — common | Shaped query or GraphQL selection |
| Response fields you'd rather not see leave the server | Exposure | Trim serialization server-side, not client-side |

## Limitations and trade-offs

- **Projection couples clients to field lists.** A `fields` param that drifts from the UI causes missing-data bugs; generated types and review keep selections honest.
- **Expansion can overcorrect.** `include=comments` on a hot list can ship megabytes of embedded relations — expanded responses need their own pagination and depth limits.
- **Purpose-built endpoints multiply.** Per-screen endpoints fix fetching and create an endpoint-sprawl maintenance bill; BFFs concentrate that sprawl into an owned layer, at the cost of running one.
- **Server-side flexibility has a price.** Arbitrary projection and expansion complicate caching (each shape is a cache key) and authorization (every combination must be safe to serve).
- **The problems are also modeling signals.** A screen that needs deep trimming or five includes may be telling you the resource shapes are wrong — sometimes the fix is [the data model](/glossary/data-modeling/), not the fetch.

## Overfetching and underfetching on Back4app

Back4app is an open-source Backend-as-a-Service (BaaS) platform that combines a managed database, auto-generated REST and GraphQL APIs, authentication, file storage, and Cloud Code serverless functions. Both fixes ship as query primitives on every SDK — the code tabs above are the whole pattern: `select()` is the sparse fieldset, `include()` is the expansion parameter, and together they turn a 1 + N waterfall into one shaped round trip against the auto-generated [REST API](/glossary/rest-api/). When clients want full control of response shape, the same data is queryable through GraphQL selection sets; when a screen needs a server-side aggregate, a Cloud Code function is a composite endpoint you write in one file. The fetch matches the screen — by idiom, not by endpoint redesign.
