GraphQL vs OpenAPI: Which Should Define Your API?
GraphQL is a query language and runtime for your API; OpenAPI is a specification for describing REST APIs. The real fight is over how you design and contract your HTTP surface. For most teams, OpenAPI-described REST wins on tooling, caching, and operational sanity.
The short answer
Openapi over Graphql for most cases. OpenAPI describes the REST you already speak, plugs into HTTP caching, gateways, and auth without custom plumbing, and generates clients in every language.
- Pick Graphql if have many heterogeneous clients (mobile, web, partners) that each need different slices of deeply nested data, and over-fetching/round-trips are a measured, real pain
- Pick Openapi Which Should Define Your Api if want a documented HTTP contract that works with every cache, gateway, SDK generator, and junior dev on day one — i.e. almost always
- Also consider: They're not exclusive. You can describe a REST API with OpenAPI today and add a GraphQL gateway later over the same services if a client genuinely needs it. Don't adopt GraphQL to look modern.
— Nice Pick, opinionated tool recommendations
They aren't even the same category
Stop pretending this is apples to apples. GraphQL is a runtime and query language — clients send queries, a single endpoint resolves them. OpenAPI is a YAML/JSON document that describes an existing REST API: paths, verbs, schemas, status codes. One executes requests; the other documents them. The honest comparison is 'GraphQL-shaped API' vs 'OpenAPI-described REST API,' because that's the actual fork in the road when you start a service. GraphQL replaces REST's design philosophy; OpenAPI formalizes it. So when someone says 'we're choosing between GraphQL and OpenAPI,' what they mean is: do we hand clients a flexible query graph, or a fixed set of well-documented endpoints? Frame it that way and the tradeoffs stop being mushy. Everything below assumes that framing, because the literal version — 'a runtime vs a spec file' — has no contest and no use to you.
Where GraphQL actually earns it
GraphQL is not a fad, and I won't pretend it is. If you have a rich, deeply-linked domain and clients that each want different cuts of it, GraphQL is genuinely better. The mobile team grabs three fields; the web dashboard grabs forty; both hit one endpoint, one round trip, no over-fetching, no versioned endpoint sprawl. The schema is introspectable, so tooling like GraphiQL and codegen is excellent out of the box. Federation lets you stitch many services behind one graph. For products that are fundamentally graphs — social networks, commerce catalogs, anything relationship-heavy — it's the right shape. GitHub and Shopify didn't ship GraphQL to be trendy; their data is a graph and their consumers are diverse. If that's you, the flexibility pays for the operational tax. The mistake is assuming you're that team when you have three endpoints and one frontend.
Where GraphQL bites you
The flexibility you bought is a bill that comes due in production. HTTP caching — the cheapest performance win on the planet — is gone, because everything is a POST to /graphql; you rebuild caching at the application layer with persisted queries and dataloaders. N+1 resolver explosions are the default until you wire batching correctly. Arbitrary nested queries are a denial-of-service vector, so now you need depth limits, complexity scoring, and rate budgets. Your API gateway, WAF, and observability stack — all built around paths and verbs — see one opaque endpoint and shrug. Error handling is weird: 200 OK with errors in the body. Authorization gets evaluated per-field, which is powerful and easy to get catastrophically wrong. None of these are unsolvable, but every one is work your team owns forever, and most teams underestimate all of them at once.
Why OpenAPI is the lower-regret default
OpenAPI wins because it sits on top of HTTP instead of around it. A described REST API gets CDN and proxy caching for free, plays nicely with every gateway, rate limiter, and auth scheme, and returns honest status codes your monitoring already understands. The spec generates typed clients and server stubs in basically every language, gives you Swagger UI docs your partners can self-serve, and powers contract testing and mocking. Junior engineers grok GET /users/123 instantly; nobody needs a week on resolver design. The tradeoff is real — over-fetching, under-fetching, and version churn when payloads change — but those are boring, well-understood problems with boring fixes. 'Boring and well-understood' is exactly what you want load-bearing in production. You can always bolt a GraphQL gateway over OpenAPI-described services later if a demanding client appears. Starting with OpenAPI keeps that door open; starting with GraphQL slams several others shut.
Quick Comparison
| Factor | Graphql | Openapi Which Should Define Your Api |
|---|---|---|
| HTTP caching | Lost by default; rebuild via persisted queries/dataloaders | Native CDN/proxy caching out of the box |
| Client data flexibility | Clients request exactly the fields they need, one round trip | Fixed payloads; over/under-fetching common |
| Tooling & ecosystem maturity | Strong codegen/introspection, younger ops ecosystem | Universal SDK gen, gateways, Swagger UI, mocking |
| Operational/security surface | Depth limits, complexity scoring, per-field authz to manage | Standard verbs/paths fit existing WAF/gateway/auth |
| Onboarding & learning curve | Resolvers, schema design, batching — real ramp | REST + a spec file; any dev gets it day one |
The Verdict
Use Graphql if: You have many heterogeneous clients (mobile, web, partners) that each need different slices of deeply nested data, and over-fetching/round-trips are a measured, real pain.
Use Openapi Which Should Define Your Api if: You want a documented HTTP contract that works with every cache, gateway, SDK generator, and junior dev on day one — i.e. almost always.
Consider: They're not exclusive. You can describe a REST API with OpenAPI today and add a GraphQL gateway later over the same services if a client genuinely needs it. Don't adopt GraphQL to look modern.
OpenAPI describes the REST you already speak, plugs into HTTP caching, gateways, and auth without custom plumbing, and generates clients in every language. GraphQL's flexibility is real but it externalizes cost: N+1 resolvers, bespoke caching, query-depth attacks, and a learning curve your whole org pays for. Unless clients genuinely need to shape arbitrary nested data, OpenAPI is the lower-regret default.
Related Comparisons
Disagree? nice@nicepick.dev