Why, after 6 years, I’m over GraphQL
🌈 Abstract
The article discusses the author's changing perspective on GraphQL, a technology they previously championed but now have reservations about. The author outlines several issues they have encountered with GraphQL, including:
- Increased attack surface and the need for robust authorization
- Challenges with rate limiting and query complexity estimation
- Performance problems related to the N+1 problem and authorization
- Coupling of business logic with the transport layer
- Overall increased complexity compared to REST APIs
The author then suggests alternatives to GraphQL, primarily recommending OpenAPI 3.0+ compliant JSON REST APIs, which they believe can provide the benefits of GraphQL (e.g., self-documenting, type-safe APIs) without the added complexity.
🙋 Q&A
[01] Attack surface
1. What are the key risks associated with exposing a GraphQL API to untrusted clients?
- The variety of attacks to consider is broader than initially expected, and mitigating them is a significant burden.
- Authorizing every field returned and/or resolved is necessary to ensure the API is secure by default, which is more complex than authorizing entire endpoints in a REST API.
- Broken Access Control has become the #1 issue on the OWASP Top 10 list, which the author attributes in part to the challenges of securing GraphQL APIs.
2. How does the author recommend mitigating the attack surface risks in GraphQL?
- Integrate with the GraphQL library's authorization framework to ensure every object returned and/or field resolved is authorized for the current user.
- This is more complex than the REST approach of authorizing entire endpoints.
[02] Rate limiting
1. What are the challenges with rate limiting in GraphQL?
- The complexity of a GraphQL query is not known until it is executed, as the query language allows for potentially large and complex queries.
- Estimating the complexity of resolving every field in the schema is a delicate affair, and the presence of cycles in the schema can lead to underestimation of the true complexity.
- This makes it difficult to accurately rate limit GraphQL queries, as a valid query may be incorrectly identified as too complex and rate limited, or an invalid query may exceed the complexity limit without being rate limited.
2. How does rate limiting in GraphQL compare to REST APIs?
- In REST APIs, rate limiting can be more straightforward, as the complexity of each endpoint is generally known and can be rate limited accordingly.
- The author provides an example of a simple Rack-based rate limiter for a REST API, which is more straightforward than the complex rate limiting required for GraphQL.
[03] Query parsing
1. What type of attack can occur during GraphQL query parsing?
- It is possible to craft an invalid query string that can cause the server to consume excessive memory by generating a large number of errors during the parsing process.
- This "out-of-memory" attack is not easily mitigated by simply limiting the payload size, as valid queries can also be larger than the smallest dangerous malicious query.
2. How does this type of attack compare to REST APIs?
- The author states that there is no equivalent REST API attack of this severity, where an invalid request can cause such a significant memory impact.
[04] Performance
1. What are the key performance issues the author has encountered with GraphQL?
- The N+1 problem, where nested fields in a list can result in multiple external data source calls.
- The N+1 problem also applies to authorization checks, where the authorization logic may need to be called multiple times for each item in a list.
- These N+1 problems are more difficult to mitigate in GraphQL compared to REST APIs, where the author suggests it is easier to hoist nested queries up to the controller level.
2. How does the author compare the performance challenges in GraphQL versus REST APIs?
- In REST APIs, the author finds it easier to address N+1 problems by hoisting nested queries to the controller level, which is a more straightforward pattern compared to the boilerplate required to address these issues in GraphQL.
[05] Coupling
1. How does the author describe the coupling of business logic with the transport layer in a mature GraphQL codebase?
- The author states that in a mature GraphQL codebase, the business logic becomes tightly coupled with the transport layer, leading to the need for extensive integration testing and making debugging more difficult.
- This is due to the various mechanisms required to solve issues like data authorization, mutation/argument authorization, and N+1 problems, which end up being implemented within the GraphQL layer.
2. How does this compare to the coupling in a REST API codebase?
- The author suggests that the REST API solutions for these types of problems are generally much simpler for a backend developer to implement and understand, leading to less coupling between the business logic and the transport layer.
[06] Complexity
1. How does the author summarize the overall complexity introduced by the various mitigations required for GraphQL?
- The author states that the various mitigations to address security and performance issues in GraphQL add significant complexity to the codebase, and that the REST solutions for these problems are generally much simpler for a backend developer to implement and understand.
2. What other issues does the author mention briefly regarding GraphQL?
- GraphQL discourages breaking changes and provides no tools to deal with them.
- The reliance on HTTP response codes in GraphQL tooling can be annoying, as a 200 status code can mean everything from "everything is OK" to "everything is down".
- Fetching all data in a single GraphQL query may not always be beneficial for response time, especially if the server is not parallelized.
[07] Alternatives
1. What are the key criteria the author suggests for when to consider using OpenAPI 3.0+ compliant JSON REST APIs instead of GraphQL?
- You control all your clients
- You have 3 or fewer clients
- Your clients are written in statically typed languages
- You are using more than one language across the server and clients
2. How does the author describe the potential benefits of using OpenAPI 3.0+ compliant JSON REST APIs compared to GraphQL?
- The author believes that if the main thing frontend developers like about GraphQL is its self-documenting, type-safe nature, then OpenAPI 3.0+ compliant JSON REST APIs can provide these benefits without the added complexity of GraphQL.
- The author is excited about the recent improvements in tooling for generating typed client code and server handlers from OpenAPI specifications, which can provide a simpler and more straightforward approach compared to GraphQL.