Back to Blog
Lesson 1 of the GraphQL: Your First GraphQL Schema & Server course
API ArchitectureJuly 12, 20264 min read

The Limitations of REST: Understanding Over-fetching and Efficiency

Discover why REST often leads to over-fetching and under-fetching, and how these limitations in API design drive the need for a transition to GraphQL.

GraphQLRESTAPI DesignOver-fetchingBackend Engineering

Welcome to the start of our journey into building robust, flexible APIs. Before we dive into the syntax of GraphQL, we need to understand the "why." You’ve likely spent time working with RESTful services, but have you ever stopped to consider why the request-response cycle often feels like a compromise?

In this lesson, we’re going to dissect the inherent rigidity of REST. By identifying the limitations of current API design, you’ll see exactly why we need a more client-centric approach.

The RESTful Resource Constraint

At its core, REST organizes data into fixed "resources"—endpoints like /users, /posts, or /comments. When you request a resource, the server decides exactly what data to return.

This is a "server-defined" model. The client has no say in the shape or size of the response. As long as your application is simple, this works fine. But as your frontend grows—or as you start supporting multiple clients like web, iOS, and Android—this rigidity becomes a bottleneck.

The Over-fetching Problem

Over-fetching occurs when a client receives more data than it actually needs. Imagine you are building a user profile page. You need the user’s username and avatarUrl.

In a typical REST API, you might call GET /users/123. The server returns:

JSON
{
  "id": 123,
  "username": "jdoe",
  "email": "jdoe@example.com",
  "avatarUrl": "https://cdn.com/avatar.jpg",
  "bio": "Software engineer and coffee enthusiast...",
  "lastLogin": "2023-10-27T10:00:00Z",
  "address": { "street": "...", "city": "..." }
}

Your app only uses two fields, but you’ve downloaded the entire user object. In a high-traffic application, transferring that extra bio, address, and lastLogin data thousands of times per second adds up to wasted bandwidth, increased latency, and higher cloud costs.

The Under-fetching and "N+1" Problem

Under-fetching is the inverse. It happens when a single endpoint doesn't provide enough information, forcing the client to make multiple follow-up requests.

If you wanted to display a user's latest posts, you might have to:

  1. Fetch the user: GET /users/123
  2. Fetch their posts: GET /users/123/posts

If you have a list of ten users and need their individual posts, you might end up firing one request for the users, and then ten separate requests for the posts. This is the classic "N+1" problem in API consumption, which creates significant performance overhead on the client.

Comparing Resource Structures

To visualize why this happens, look at how the data is structured in these two paradigms:

FeatureREST APIGraphQL
Data ShapeDefined by the serverDefined by the client
EndpointsMultiple (one per resource)Single (one for the whole graph)
Data FetchingUsually fixed responsesPrecise selection sets
VersioningOften requires /v1/, /v2/Schema evolution (no versions)

In REST, you are often forced to choose between creating specialized endpoints (which leads to "endpoint explosion") or living with the overhead of generic, heavy responses. GraphQL solves this by allowing the client to specify exactly which fields it wants, eliminating both over-fetching and under-fetching in one go.

Hands-on Exercise: Identifying Inefficiency

Find an existing API endpoint in one of your current projects (or use a public API like JSONPlaceholder).

  1. Observe the Payload: Call the endpoint and look at the JSON response.
  2. Map the Usage: Write down every field returned by the server.
  3. Audit the Need: Highlight only the fields your UI actually displays on the screen.
  4. Calculate Waste: Estimate the percentage of the payload that is "dead weight" (data that is downloaded but never rendered).

Thought experiment: If you were to redesign this as a GraphQL query, what would your selection set look like?

Common Pitfalls

  • Treating REST as "Wrong": REST isn't broken; it's just designed for a different set of trade-offs. It's excellent for caching and simple resource-based operations. Don't throw away REST for static assets or simple CRUD; reserve GraphQL for complex data requirements.
  • Ignoring the Network: Many developers ignore over-fetching because "internet is fast." However, on mobile devices with high latency or unstable connections, every kilobyte saved significantly improves the perceived performance of your application.
  • Thinking GraphQL is an "Endpoint": A common mistake is to think of GraphQL as just another REST endpoint. It is a query language that operates over a single endpoint, which requires a shift in how you handle errors and caching—topics we will cover as we progress.

Summary

REST provides a predictable, cacheable structure, but it forces the client to accept whatever the server deems relevant. This leads to inefficient data transfer through over-fetching and unnecessary request chains due to under-fetching. GraphQL shifts the power to the client, allowing for precise, performant data retrieval.

In our next lesson, we will begin our transition into the GraphQL ecosystem by exploring the philosophy behind the single-endpoint model and how the schema acts as a contract between your client and server.

Up next: The GraphQL Philosophy

Similar Posts