GraphQL versus REST is one of those debates that generates more heat than light, usually because it's argued in the abstract instead of against a specific problem. Both are perfectly capable ways to build an API; they optimize for different things, and choosing well is about matching the paradigm to your actual clients and data, not about which is newer or which a conference talk praised. REST models your API as resources with standard HTTP verbs and is boringly well-understood, cache-friendly, and easy to operate. GraphQL exposes a single flexible endpoint where the client asks for exactly the fields it wants in one round trip, which is genuinely powerful for rich, client-driven UIs — and which quietly relocates a pile of complexity from the client to your server. The right question is never 'which is better' but 'which set of tradeoffs fits the API I'm actually building, and for whom.'
The problem GraphQL actually solves
GraphQL's core value is real, so it's worth stating precisely: it eliminates over-fetching and under-fetching for clients whose data needs vary. With REST, a mobile screen that needs a user's name, their last three orders, and each order's status often means either one fat endpoint that returns far more than any single screen uses, or three or four round trips stitched together on the client. GraphQL lets the client describe exactly the shape it wants in a single request and get precisely that back — no more, no less. For a product with many different clients (web, iOS, Android, partners) all reading the same underlying data in different shapes, or a UI that iterates fast and keeps needing new field combinations, that flexibility is a genuine productivity win and the strongest reason to reach for it. If your clients' needs are uniform and stable, that win is smaller than the marketing suggests.
The costs GraphQL quietly adds
The flexibility isn't free — it moves work to the server, and teams that adopt GraphQL for the demo often meet these costs in production. Caching is the big one: REST leans on mature HTTP caching (CDNs, cache headers, ETags) because a GET to a URL is cacheable, while GraphQL's single POST endpoint with arbitrary queries defeats that model and pushes you toward application-level and persisted-query caching you have to build and reason about — a topic that connects directly to your broader caching strategy. The N+1 problem is the next trap: a nested query for 'these 50 users and each of their orders' naively fires one query per user unless you add batching (DataLoader-style) — easy to forget and brutal under load. And because clients can ask for anything, you inherit query-complexity and depth limits to stop a single expensive query from taking down your database. None of these are dealbreakers; they're the work you're signing up for.
Where REST is still the right call
REST remains the correct default for a large class of APIs, and choosing it is not settling for less. If your API is primarily resource-oriented CRUD, if your main consumers are third-party developers who expect the predictability and tooling of REST, or if aggressive HTTP caching materially helps your performance and cost, REST is simpler to build, operate, secure, and document. It fits naturally with the versioning and deprecation discipline that public APIs need, and its request-per-resource shape makes rate limiting, monitoring, and debugging straightforward — you can read the access log and know what happened. A great REST API with thoughtful resources, consistent conventions, and good pagination will serve most SaaS products for years. 'Boring and well-understood' is a feature when the thing has to run reliably and be operated by a small team.
Security and operations differ more than people expect
The two paradigms don't just differ in ergonomics; they change how you secure and operate the API. With REST, authorization maps cleanly onto endpoints and resources, and rate limiting is per-route. With GraphQL, a single query can traverse many types and objects in one request, so authorization has to be enforced at the field and object-resolver level — it's easy to accidentally expose data through a nested relationship nobody thought to guard, which is the same object-level authorization failure mode that plagues APIs generally, just harder to see. Rate limiting shifts from 'requests per endpoint' to 'query cost per request,' since one GraphQL call can be trivial or ruinously expensive. Observability changes too: everything is a POST to /graphql, so your logs need query-aware instrumentation to tell a cheap read from the query that's hammering your database. These are solvable, but they're real design work you should budget for up front, not discover in an incident.
A pragmatic way to decide (and to combine them)
The decision doesn't have to be all-or-nothing, and the best answer is often both in their place. A useful default: reach for GraphQL when you have diverse clients with varying, fast-changing data needs against a richly connected data graph, and you have the server-side maturity to handle caching, batching, and per-field authorization. Choose REST when your API is resource-oriented, consumed by third parties, or benefits heavily from HTTP caching and operational simplicity. Many mature systems do both — a REST API for public/partner integrations and webhooks, and a GraphQL layer (often a backend-for-frontend) serving the company's own rich first-party apps. What you should not do is adopt GraphQL because it's fashionable and then reimplement REST's caching and simplicity badly on top of it, or contort a genuinely graph-shaped, multi-client product through a dozen bespoke REST endpoints. Pick against your clients and your data, and let each paradigm do what it's good at.
How Infiniti Tech Partners approaches API design
We choose the API paradigm from your clients and your data, not from fashion. When your product has diverse, fast-moving clients against a connected data graph, we build GraphQL properly — with the caching, DataLoader-style batching, query-complexity limits, and per-field authorization that keep it fast and safe under real load. When your API is resource-oriented or serves third-party developers, we build clean, well-versioned REST that's easy to cache, secure, and operate. Often we combine them deliberately: REST for public and partner surfaces, a GraphQL BFF for your own apps. Either way we design for the parts teams underestimate — authorization, caching, rate limiting, and observability — so the API is a pleasure to consume and boring to run, which is exactly what you want from infrastructure your product depends on.
Frequently asked questions
Should I use GraphQL or REST for my API?
Choose against your clients and your data, not fashion. Reach for GraphQL when you have diverse clients with varying, fast-changing data needs against a richly connected data graph and the server-side maturity to handle caching, batching, and per-field authorization. Choose REST when your API is resource-oriented CRUD, consumed by third-party developers, or benefits heavily from HTTP caching and operational simplicity. Many mature systems do both — REST for public and partner surfaces, and a GraphQL backend-for-frontend serving their own rich first-party apps.
What are the downsides of GraphQL compared to REST?
GraphQL's flexibility moves work to the server. Caching is harder because a single POST endpoint with arbitrary queries defeats mature HTTP caching, pushing you toward application-level and persisted-query caching you have to build. The N+1 problem means a nested query fires one database query per item unless you add DataLoader-style batching, and because clients can ask for anything, you need query-complexity and depth limits so one expensive query can't take down your database. None are dealbreakers, but they're real work you sign up for.
Is GraphQL harder to secure than REST?
It's different, and easier to get wrong if you're not deliberate. With REST, authorization maps cleanly onto endpoints and resources and rate limiting is per-route. With GraphQL, a single query can traverse many types and objects in one request, so authorization must be enforced at the field and object-resolver level — it's easy to accidentally expose data through a nested relationship nobody guarded. Rate limiting also shifts from requests-per-endpoint to query-cost-per-request, and observability needs query-aware instrumentation since everything is a POST to /graphql.
Related reading
API Design and Versioning: Building Interfaces You Won't Regret
How to design APIs that survive contact with real integrators — resource modeling, consistency, pagination, and a versioning and deprecation strategy that lets you evolve without breaking every customer.
EngineeringEvent-Driven Architecture: Queues, Streams, and When Async Actually Helps
When to reach for queues and event streams instead of synchronous calls — the difference between a queue and a log, the patterns that make async reliable (outbox, idempotency, dead-letter queues), and the failure modes teams underestimate.
EngineeringPlatform Engineering: Building an Internal Developer Platform That Pays Off
What platform engineering and internal developer platforms actually solve for growth-stage SaaS — golden paths, self-service, and paved roads — and how to build one without creating a bottleneck team.