bessey.dev
Performance
### Authorisation I think this is the most widely understood risk of GraphQL, so I won’t go into too much depth here. TLDR: if you expose a fully self documenting query API to all clients, you better be damn sure that **every field** is authorised against the current user appropriately to the context in which that field is being fetched. Initially authorising **objects** seems like enough, but this quickly becomes insufficient. For example, say we are the ~~Twitter~~ X 🙄 API: … This is not a unique problem to GraphQL, and actually the strict GraphQL resolution algorithm has allowed most libraries to share a common solution: the Dataloader pattern. Unique to GraphQL though is the fact that since it is a query language, this can **become** a problem with no backend changes when a client modifies a query. As a result, I found you end up having to defensively introduce the Dataloader abstraction everywhere *just in case* a client ends up fetching a field in a list context in the future. This is a lot of boilerplate to write and maintain. … ``` class UserType < GraphQL::BaseObject field :handle, String field :birthday, authorize_with: :view_pii end class UserPolicy < ApplicationPolicy def view_pii? # Oh no, I hit the DB to fetch the user's friends user.friends_with?(record) end end ``` … This is actually trickier to deal with than our previous example, because authorisation code is not always run in a GraphQL context. It may for example be run in a background job or an HTML endpoint. That means we can’t just reach for a Dataloader naively, because Dataloaders expect to be run from within GraphQL (in the Ruby implementation anyway). … - GraphQL discourages breaking changes and provides no tools to deal with them. This adds needless complexity for those who control all their clients, who will have to find workarounds. - Reliance on HTTP response codes turns up everywhere in tooling, so dealing with the fact that 200 can mean everything from everything is Ok through to everything is down can be quite annoying.
Related Pain Points4件
Sensitive data exposure and authorization complexity
8GraphQL's unified endpoint and flexible query structure can inadvertently expose sensitive data. Without strict authentication and authorization checks at the field level, unauthorized users can query restricted information. Field-level security is complex, error-prone, and can cause entire requests to fail.
GraphQL authorization logic in non-GraphQL contexts
6Authorization code written for GraphQL contexts often cannot be reused in background jobs or HTML endpoints, because Dataloaders (the common solution to N+1 problems) are GraphQL-specific. This forces developers to implement separate authorization logic for different execution contexts.
Lack of standardized error handling
5GraphQL returns HTTP 200 for most responses even when errors occur, making it difficult for clients to programmatically determine error nature. Errors are embedded in the response body without standardized error codes, forcing developers to implement custom error-handling logic and parse fragile error message strings.
GraphQL breaking change management without tooling support
5GraphQL discourages breaking changes but provides no built-in tools to manage them, forcing developers who control all their clients to find workarounds and add needless complexity.