DEV Community

Cover image for Lesson 1.3 - GraphQL for Screen Complexity and App Changes
Subrata Kumar Das
Subrata Kumar Das

Posted on

Lesson 1.3 - GraphQL for Screen Complexity and App Changes

Good morning, class! Welcome back. In our last lesson, we explored how GraphQL fundamentally addresses the issues of over-fetching and under-fetching.

Today, we are going to connect those architectural concepts directly to real-world React Native screen complexity. Our Goal today is to understand how GraphQL simplifies your codebase when a screen demands multiple data sources, when different screens require entirely unique variations of the same resource, and when your mobile application changes and evolves over time.


1. Problem One: Codebase Complexity via Multiple API Calls

Making multiple API calls is not inherently wrong, but from a mobile product engineering perspective, it introduces a significant amount of state-tracking boilerplate inside your components.

In a traditional React Native screen driven by REST, managing multiple endpoints often forces you into a state management pattern that looks like this:

const [products, setProducts] = useState([]);
const [categories, setCategories] = useState([]);
const [loadingProducts, setLoadingProducts] = useState(false);
const [loadingCategories, setLoadingCategories] = useState(false);
const [productsError, setProductsError] = useState<string | null>(null);
const [categoriesError, setCategoriesError] = useState<string | null>(null);

Enter fullscreen mode Exit fullscreen mode

Class, look closely at this block of code. We haven't even written a single line of JSX or rendered any UI yet, and our component is already drowning in individual state hooks just to track the network layer.

The moment a single screen relies on multiple independent asynchronous requests, you are forced to programmatically answer complex state orchestration questions:

  • Is the entire screen considered to be in a loading state, or are individual sub-sections loading independently?
  • What happens visually if the product list resolves successfully, but the category chip request fails?
  • When a user triggers a pull-to-refresh action, should it aggressively retry every single request?
  • Should a localized network failure on a non-critical widget block the user from interacting with the rest of the screen?

2. GraphQL Fix One: Consolidated Screen-Level Operations

Instead of forcing your UI components to orchestrate several disconnected REST endpoints, a single GraphQL operation lets you fetch the precise combination of data pieces your screen needs in one single network round-trip:

query HomeScreenData {
  categories
  products(page: 1, size: 10) {
    meta {
      total
      page
      pages
    }
    data {
      id
      name
      price
      imageUrl
      category
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

Now, let's be entirely clear: this does not magically erase the necessity for loading spinners or error screens. You still need to design and implement resilient UI fallbacks.

However, it vastly simplifies your frontend state logic. It collapses multiple asynchronous workflows down into one single operation for your component to reason about, dramatically reducing the state variables you need to manually track.


3. Problem Two: Screen-Specific Data Requirements

As an application grows, different mobile screens will inevitably demand entirely different variations of the exact same data model. Let’s map out a standard e-commerce flow:

  • Product List Screen Layout: Only needs id, name, price, and imageUrl.
  • Product Details Screen Layout: Requires id, name, price, imageUrl, description, brand, rating, discount, and inStock.
  • Cart Summary Screen Layout: Demands id, name, price, quantity, and inStock.

In a traditional REST architecture, backend teams typically handle these evolving variations by creating specific hyper-targeted endpoints (like /api/v1/products/cart-view) or by adding heavy configurations of query parameters (like /products?include=details).

While this can get the job done, over time it causes your API surface area to balloon into a tangled, unmanageable mess simply because every new screen design requires a slightly modified data payload shape.


4. GraphQL Fix Two: Component-Driven Selection Sets

GraphQL shifts data model ownership back to the client application. Instead of waiting on backend adjustments, each specific screen explicitly declares its own data requirements.

Furthermore, you can parameterize these operations dynamically using GraphQL Variables:

query ProductListScreen($page: Int!, $size: Int!, $category: String) {
  products(page: $page, size: $size, category: $category) {
    meta {
      total
      page
      pages
    }
    data {
      id
      name
      price
      imageUrl
      inStock
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

To execute this operation with dynamic filtering, you pass a separate Variables object alongside the query:

{
  "page": 1,
  "size": 10,
  "category": "fruits"
}

Enter fullscreen mode Exit fullscreen mode

The underlying structural template of the query remains completely stable. The runtime variables change fluidly based on user interaction. This exact paradigm is exactly what makes building mobile filters, search views, and infinite-scrolling lists highly predictable and clean.


5. Problem Three: The Production Reality of App Versioning

Unlike web applications where a new deployment updates all users instantly, mobile applications stay installed on user devices for months or even years.

If a backend team alters or refactors a REST response payload, older, un-updated installations of your mobile app out in the wild can instantly crash. Because of this, mobile engineering teams are traditionally forced to implement strict, complex API versioning policies (like /api/v1/ vs /api/v2/).

GraphQL does not replace architectural discipline, but it offers natural protection against breaking changes because the client must explicitly request the fields it intends to use. If an older mobile app build doesn't know about a new rating field, it simply won't request it. If a newer build needs to display it, you append it inline without affecting old versions:

query ProductCards {
  products(page: 1, size: 10) {
    data {
      id
      name
      price
      rating # Added seamlessly for new app versions
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

Because the backend server knows exactly which fields are being requested by current active users, the client app is no longer tightly coupled to a monolithic, fixed response shape.


6. The Blueprint: Predictable Response Mirroring

One of the most beginner-friendly and elegant aspects of working with GraphQL is that the response payload shape directly mirrors your request query structure.

Let’s trace an active transaction:

Client Query Document:

query CategoriesAndProducts {
  categories
  products(page: 1, size: 2) {
    data {
      id
      name
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

JSON Server Response:

{
  "data": {
    "categories": ["Fruits", "Vegetables"],
    "products": {
      "data": [
        { "id": 1, "name": "Apple" },
        { "id": 2, "name": "Tomato" }
      ]
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

While the literal data values inside the strings and arrays will change based on database records, the structural nesting matches your query word-for-word, making data binding inside React Native components entirely intuitive.


7. Guided Practice in GraphiQL

Before we jump back into our mobile IDEs, let's build muscular memory by sandbox testing this query layout inside GraphiQL:

  1. Navigate your web browser to https://backend.ecom.subraatakumar.com/graphiql.
  2. Paste the parameterized ProductListScreen query into the main editor panel.
  3. Open the Query Variables drawer at the bottom left and pass valid page/category variables.
  4. Hit the Play button to execute.
  5. Experiment: adjust the category string to another value and re-run.
  6. Delete imageUrl from the selection set and observe the updated JSON structure.
  7. Append rating to the selection set, execute, and verify the structural response mirrors your query change instantly.

This iterative feedback loop is the core day-to-day workflow of a productive GraphQL developer.


8. React Native Low-Level Implementation

Let’s observe how this complete multi-resource request compiles into a low-level JavaScript module using our temporary native fetch client:

async function loadProductList(page: number, category?: string) {
  const response = await fetch("https://backend.ecom.subraatakumar.com/graphql", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      query: `
        query ProductListScreen($page: Int!, $size: Int!, $category: String) {
          products(page: $page, size: $size, category: $category) {
            meta {
              total
              page
              pages
            }
            data {
              id
              name
              price
              imageUrl
              inStock
            }
          }
        }
      `,
      variables: {
        page,
        size: 10,
        category: category ?? null,
      },
    }),
  });

  const json = await response.json();

  if (json.errors) {
    throw new Error(json.errors[0].message);
  }

  return json.data.products.data;
}

Enter fullscreen mode Exit fullscreen mode

Check Your Understanding

Before we wrap up this theory session and head into our practical labs, make sure you can answer these three questions clearly:

  1. How do multiple independent REST calls compound the structural complexity of component loading and error states in React Native?
  2. What does it mean for a screen to "own its data requirement," and how does this prevent API endpoint bloat on the backend?
  3. What is the relationship between the structural layout of a GraphQL query document and the eventual JSON payload returned by the server?

Great work today, class. Review your notes, spend 10 minutes testing variations in the GraphiQL playground, and I will see you all in our next session!


Connect with me:

Top comments (0)