I Am Done With Graph QL After 6 Years
Based on The PrimeTime's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.
GraphQL’s main downside is not its concept of queries, but the security and performance burden created by letting untrusted clients send arbitrary query shapes.
Briefing
GraphQL’s biggest problem isn’t that it’s “bad”—it’s that exposing a query language to untrusted clients dramatically expands the security and performance burden, and the mitigations add enough complexity that many teams end up worse off than if they used a conventional, typed API. After years of championing GraphQL, the speaker says the tradeoffs become unacceptable once security, maintainability, and operational reliability matter more than developer convenience.
A central risk is attack surface. Because clients can request arbitrary shapes of data, attackers can craft queries that strain authorization logic, overwhelm CPU, or amplify resource usage. Authorization is the most widely understood threat: a fully self-documenting GraphQL endpoint requires field-by-field authorization in the context of the current user, not just coarse checks at the object level. The speaker argues that this is harder than REST’s typical model, where developers authorize endpoints rather than every field in every nested context.
Denial-of-service risk is framed as even more severe. GraphQL servers can’t assume requests are equally “expensive,” and there’s no simple, universal limit on query size or computational cost. Even with an empty schema, introspection types can be cyclic, enabling valid queries that return large JSON payloads or force the server to spend significant CPU time parsing and resolving. The speaker describes testing an attack against a popular GraphQL API explorer and seeing 500 responses after about ten seconds from a very small query, with the implication that a short curl loop can effectively “DoS yourself.”
Mitigations exist—query complexity limits, rate limiting, maximum depth, and stricter schema design—but they’re delicate to implement correctly. Complexity estimation can be wrong, especially with cyclic schemas (e.g., tags related to related tags), where errors can compound exponentially. Default maximum depth settings (the speaker cites GraphQL Ruby’s default of 13) help but don’t fully address the underlying issue: deeply nested queries are only one dimension of cost.
Performance concerns extend beyond security. The speaker highlights the N+1 problem caused by nested resolvers that trigger repeated database or HTTP calls, and argues that GraphQL often pushes data-fetching logic into the transport layer. Once authorization is integrated into resolvers, it can create a new class of N+1-like performance issues where authorization checks themselves hit the database repeatedly. Debugging also becomes harder because much of the behavior—authorization, data loading, error handling—occurs inside the GraphQL framework, leaving developers to interpret stack traces embedded in GraphQL error responses.
The conclusion is a conditional recommendation: GraphQL may still make sense when teams control all clients, use a statically typed language, and need GraphQL’s type-safe, self-documenting query experience. Otherwise, the speaker prefers OpenAPI 3.0-compliant JSON REST APIs, with tooling that generates typed clients and server handlers from specs. The broader message is that GraphQL’s flexibility can be a “hype-cycle” fit for early stages, but in sufficiently complex environments it can force teams into building and maintaining expertise—and infrastructure—to manage risks that simpler API styles avoid.
Cornell Notes
GraphQL’s flexibility comes with a security and performance tax because clients can send arbitrary queries that are hard to bound. The most serious issues highlighted are authorization complexity (field-by-field checks in user context) and denial-of-service risk (queries with unpredictable cost, including introspection-based attacks). Complexity limits, depth limits, and rate limiting can reduce harm, but they’re tricky to implement correctly—especially with cyclic schemas where estimates can be wildly wrong. The result is more framework-driven logic, harder debugging, and more integration testing. For many teams, the speaker recommends OpenAPI 3.0 REST with typed client/server generation as a simpler alternative once operational constraints dominate.
Why does exposing GraphQL to untrusted clients increase risk compared with typical REST endpoints?
What makes denial-of-service risk feel uniquely dangerous in GraphQL?
How do query complexity limits and maximum depth help, and why are they fragile?
What performance problems does GraphQL introduce beyond security?
Why does GraphQL debugging and testing become more difficult in practice?
What alternative does the speaker recommend, and under what conditions?
Review Questions
- What specific authorization requirement makes GraphQL harder to secure than endpoint-based REST authorization?
- Explain how cyclic schemas can break query complexity estimation and lead to under-mitigated attacks.
- List two reasons GraphQL can increase integration testing and debugging effort compared with REST.
Key Points
- 1
GraphQL’s main downside is not its concept of queries, but the security and performance burden created by letting untrusted clients send arbitrary query shapes.
- 2
Field-by-field authorization in user context is required for safe GraphQL, and it’s harder than authorizing endpoints in a REST-style API.
- 3
Denial-of-service risk in GraphQL stems from unpredictable query cost, including parsing and introspection-driven attacks.
- 4
Query complexity limits and maximum depth can mitigate attacks, but they’re fragile—especially with cyclic schemas where estimates can compound exponentially.
- 5
GraphQL can worsen N+1 performance issues because nested resolvers and authorization checks may repeatedly hit databases or external services.
- 6
Framework-driven execution (data loaders, authorization, error handling) shifts failures into GraphQL error responses, making debugging and integration testing more painful.
- 7
A common alternative is OpenAPI 3.0 REST with typed client/server generation, particularly when teams don’t control all clients or don’t want GraphQL’s operational complexity.