We designed our core public API with REST principles because it was the industry standard. Every resource had a predictable URL structure, standard HTTP verbs, and JSON payloads. It powered our web frontend and mobile apps smoothly for two years. Then, our mobile teams started hitting a wall.

Our mobile app updates were constantly bottlenecked because the REST endpoints either over-fetched data—forcing phones to download massive JSON payloads with fields they didn't need—or under-fetched, requiring three or four sequential HTTP requests just to render a single user profile screen.

We tried adding custom query parameters, versioning routes like /v2/users/details-with-posts, and building backend-for-frontend (BFF) proxies. But maintenance quickly turned into a spaghetti of endpoint wrappers. We started asking: should we migrate our core client-facing API to GraphQL?

The API performance audit

We ran a two-week evaluation comparing our legacy REST setup against a new GraphQL gateway handling typical mobile app data-fetching patterns.

Mobile profile screen fetch metrics (Over 5,000 simulated client requests)

+---------------------------+------------------+-------------------+ | Metric              | REST API         | GraphQL API       | +---------------------------+------------------+-------------------+ | Network payload size      | 142 KB           | 18 KB             | | Number of round-trip HTTP | 4 requests       | 1 request         | | p95 latency (mobile 4G)   | 850 ms           | 220 ms           | | Frontend dev iteration   | Slow (backend-dep)| Fast (client-driven)| | Endpoint versioning       | Constant maintenance| Single schema     | +---------------------------+------------------+-------------------+

GraphQL drastically reduced network payload sizes and eliminated the round-trip latency penalty on flaky mobile connections by allowing clients to request precisely what they needed in a single query.

More importantly, it decoupled frontend development from backend route changes, letting mobile engineers update UI components without waiting for custom backend endpoints to be deployed.

We reduced mobile payload sizes by 87%, cut client-side request latency down to a fraction, and finally stopped maintaining dozens of redundant REST wrapper endpoints.

How we implemented GraphQL alongside REST

Instead of a disruptive rewrite, we adopted a gradual migration strategy. Here's the approach we used:

  1. Deploy a GraphQL gateway in front of existing microservices

    We set up an Apollo Server gateway that wrapped our existing REST services, translating incoming GraphQL queries into efficient internal service calls.

    type User { id: ID! name: String! posts(limit: Int): [Post] } type Query { viewer: User }

  2. Implement DataLoader to solve the N+1 query problem

    To prevent inefficient database lookups when resolving nested relationships, we integrated batching and caching patterns using DataLoader.

  3. Establish strict query complexity and depth limits

    To protect our database from malicious or deeply nested recursive queries, we configured strict query complexity analysis and depth-limiting rules on the server.

  4. Migrate clients incrementally

    We migrated the mobile app to GraphQL first while keeping our public third-party developer API on standard REST, avoiding disruption for external partners.

What we gave up (and what we gained)

We lost the dead-simple native HTTP caching capabilities built into web browsers and CDNs, which don't map cleanly to POST-based GraphQL queries. But we gained:

  • Client Flexibility – mobile and web frontends could query custom data shapes without server changes.
  • Strong Typing & Introspection – auto-generated documentation and strongly typed client SDKs drastically reduced integration bugs.
  • Fewer Round Trips – consolidated data fetching optimized performance over high-latency cellular networks.

Worth noting: GraphQL introduces overhead with schema design, caching complexity, and monitoring query performance. If your project is a simple CRUD application or a public developer platform meant for wide third-party consumption, standard REST remains the cleaner choice.

The takeaway

Adopting GraphQL for our client-facing layers transformed how our product teams ship features. Mobile iteration cycles are faster, network overhead is minimal, and our API layer is no longer choked by custom endpoint bloat.

The lesson: choose your API paradigm based on who is consuming the data and how they interact with it, rather than blindly following architectural trends.