Skip to main content

GraphQL needs new thinking

· 6 min read
Ramnivas Laddad
Co-founder @ Exograph

Backend developers often find implementing GraphQL backends challenging. I believe the issue is not GraphQL but the current implementation techniques.

This post is a response to Why, after 6 years, I'm over GraphQL by Matt Bessey. I recommend that you read the original blog before reading this. The sections in this blog mirror the original blog, so you can follow them side-by-side.

Before starting Exograph, we implemented several GraphQL backends and faced many of the same issues mentioned in the blog. Exograph is our attempt to address these issues and provide a better way to implement GraphQL. Let's dive into the issues raised in the original blog and how Exograph addresses them.

Attack surface

The expressive nature of GraphQL queries makes it attractive to frontend developers. However, this exposes a large attack surface, where a client can craft a query that can overwhelm the server.

Authorization

Problem: GraphQL authorization is a nightmare to implement.

Solution: A declarative way to define entity or field-level authorization rules.

While authorization is an issue with any API, it is particularly challenging with GraphQL. A typical REST API implementation considers access to only one resource at a time. In contrast, GraphQL implementations need to process queries that can ask for multiple resources. Processing such a query while enforcing authorization rules for each query field is a backend developer's nightmare.

Exograph provides a declarative way to define authorization rules at the entity or field level. Exograph's runtime will enforce them. And by default, Exograph assumes that no one can access an entity unless explicitly allowed, forcing an explicit decision on the developer.

For example, the following rule ensures that a non-admin user can query any department but only published products. An admin user, in contrast, can query or mutate any product or department. With this structure in place, no matter how the query is structured (get products along with their department or departments along with their products), the client can only access the resources they are authorized to.

context AuthContext {
@jwt role: String
}

@postgres
module EcommerceDatabase {
@access(query=self.published || AuthContext.role=="admin",
mutation=AuthContext.role=="admin")
type Product {
// ...
published: Boolean
department: Department
}

@access(query=true, mutation=AuthContext.role=="admin")
type Department {
// ...
products: Set<Product>?
}
}

In a way, Exograph allows a REST-like thought process, where you consider authorization of each resource in isolation. Exograph's query engine ensures that no matter how the query is structured, the client will only get access to the authorized resources.

Rate limiting

Problem: GraphQL queries can ask for multiple resources in a single query, overwhelming the backend.

Solution: Trusted documents, query depth limiting, and rate limiting.

Since GraphQL can ask for multiple resources in a single query, it is easy to overwhelm the backend. For example, a client can issue a single deeply nested query, which will cause many trips to the database.

Exograph addresses this issue in multiple ways:

Query Parsing

Problem: GraphQL queries can overwhelm the parser.

Solution: The same solution as above.

GraphQL queries express the client's intent at a finer level than REST APIs, requiring the backend to parse the query and present itself as an attack surface.

Exograph's solution for rate limiting also addresses this issue. For example, by allowing only trusted documents, you can ensure clients cannot make arbitrary queries and overwhelm the backend.

Performance

In a typical traditional GraphQL implementation, the backend developer will write a resolver for each entity to fetch the data, which can lead to the N+1 problem, where the backend makes multiple queries to fetch the data. Balancing authorization and the N+1 problem can be tricky.

Data fetching and the N+1 problem

Problem: GraphQL queries can lead to the N+1 problem.

Solution: Defer data fetching to a query planner.

This is a classic issue with GraphQL implementation, where a client can ask for multiple resources in a single query. A common solution is data loader, which batches the queries and fetches the data in fewer round trips but requires careful implementation (especially when considering authorization).

Exograph relieves this burden through its query engine. Exograph's query planner maps a GraphQL query to (typically) a single SQL query.

Authorization and the N+1 problem

Problem: Balancing authorization and the N+1 problem can be tricky.

Solution: Make authorization rules a part of the query planning process.

Even if you were to use a data loader to solve the N+1 problem, once you also consider authorization, you end up fighting modularity (see the next point) against performance.

Exograph's query engine incorporates authorization rules while planning the query, so the same query planning process solves this problem.

Coupling

Problem: GraphQL makes it quite hard to keep code modular.

Solution: Reduce the amount of code by leaning on a declarative approach.

GraphQL makes it quite hard to keep code modular, especially when dealing with authorization and performance, which leads to code-tangling and code-scattering to enforce authorization rules.

First, in Exograph, you write very little code. You define your data model and authorization rules, and Exograph plans and executes queries for that model.

Second, Exograph's declarative nature allows you to express authorization rules alongside the data model. Exograph query engine—and not your code—enforces these rules.

Complexity

Problem: GraphQL makes balancing authorization, performance, and modularity tricky.

Solution: Simplify implementing GraphQL through a declarative way to define data models and authorization rules.

GraphQL can be complex to implement, balancing authorization, performance, and modularity. Left unchecked, this can lead to a complex and hard-to-maintain codebase with possible security issues.

Exograph simplifies implementing GraphQL through a declarative way to define data models and authorization rules. Exograph query engine takes care of planning and executing queries efficiently. It also provides tools for dealing with various aspects, from quick development feedback to production concerns.

Conclusion

We believe that with the right level of abstraction, you can get secure, performant, and modular backend implementation. With Exograph, we provide a declarative approach to get all the benefits of GraphQL without the downsides.

What do you think? Reach out to us on Twitter or Discord with your feedback.

Share: