One endpoint can trigger many separate data accesses
GraphQL describes requested fields within an operation. The application resolves them through execution logic, often using resolvers. A resolver supplies a field's value or delegates to another layer. Access to /graphql is therefore only the entrance, not the entire authorisation decision.
The following synthetic document requests a report's title and comments. It assumes a suitable fictional schema; it has not been executed against a GraphQL server.
query ReportOverview($id: ID!, $first: Int!) {
report(id: $id) {
title
comments(first: $first) {
nodes {
text
}
}
}
}
The server must decide, among other things: may this person see the report? May they see its comments? How many comments per query are permitted? A required Int! argument proves neither a sensible range nor permission. GraphQL validation checks the schema contract; additional value and business rules remain necessary. GraphQL Specification, validation and execution.
Share authorisation logic with other access paths
Establish verified caller context before execution. Use it for every protected object and field access. If a report is reachable through report(id: ...), a team list or a nested project, these paths must enforce the same business rule.
The official GraphQL documentation recommends placing authorisation in shared business logic. Resolvers call that logic instead of copying divergent rules. A custom schema directive such as @auth works only if your implementation actually gives it appropriate enforcement. GraphQL: Authorization.
Check mutation, object and modifiable fields separately. Permission to call a mutation does not automatically allow changing every referenced object. The same applies to tenant context and cached results: a cache hit must not import data from another caller or tenant context. See the foundations in BOLA/IDOR, field permissions and tenant isolation.
Depth, breadth and actual cost are different limits
A shallow query may contain many expensive fields. A short query may request large lists. The same HTTP request count can therefore trigger very different workloads.
On narrow screens, scroll the table sideways.
| Limit | What it bounds | What remains unresolved |
|---|---|---|
| Maximum depth | Nesting of fields and relationships | Broad queries and expensive individual resolvers |
| Field, alias or operation breadth | Many requested parts within one operation | Cost of an individual field |
| Pagination and maximum list length | Data volume per list field | Nested lists, total costs and database work |
| Cost budget | Estimated work based on fields and arguments | Difference between model and actual execution |
| Time, size and concurrency limits | Runtime, response volume or concurrent work | Business abuse of permitted operations |
Enforce pagination server-side. A first argument needs an upper limit; its mere presence does not bound a list. Account for the total effect of nested lists. Cost models can weight expensive fields and include quantity arguments; they need comparison with your actual data access. No universally safe maximum depth exists. GraphQL: Security, Demand control, OWASP GraphQL.
Measure legitimate operations from your clients in a suitable environment and choose explainable budgets with limited headroom. Also check whether a timeout actually stops expensive downstream work. A broken HTTP connection need not mean a database query or external job has stopped.
Do not confuse aliases, multiple operations and batching
An alias renames a result field. Multiple aliases may request different resolutions of the same field within one operation. Account for actual field work rather than counting only the endpoint name.
A document may also contain several named operations. Under the GraphQL specification, this does not mean all run together: the request selects one operation; where several exist, the operation name is required. Optional batching differs: an implementation accepts multiple GraphQL requests through one transport call. Explicitly establish this behaviour with your library and server contract. GraphQL Specification, section 6.1.
If you support batching, set per-operation and whole-batch limits. Check authentication, cost accounting and error handling in that processing. Disabling an unnecessary batch feature can simplify the contract; aliases and other regular GraphQL features remain separate concerns.
Known documents can help but do not replace permissions
For exclusively first-party clients, pre-checked and explicitly allowed documents can restrict permitted operations. The server then accepts only registered document identifiers or their approved operations. The process must fit your release workflow: new client versions need their permitted documents in time.
Ordinary persistence or a hash cache is not automatically such an allowlist. If arbitrary new documents may be registered at runtime, the additional trust boundary does not exist in the same way. Known documents still receive variable values: object ID, quantity and business values continue to need their own checks.
For an API with independent external clients, predefining every operation is often inappropriate. Schema, value, permission and resource rules support use here. The decision should preserve the intended API purpose instead of silently disabling its central benefit.
Handle introspection and error data deliberately
Introspection makes the schema queryable and may be intended for developer tools or external users. Disabling it may reduce immediate discoverability but replaces neither authorisation nor resource limits. Error messages may also reveal schema details. Decide which development information should be visible based on clients and operating environment.
GraphQL responses may contain partial data and errors together. Assess data, errors and the specific contract; neither HTTP 200 nor any error alone proves whether unauthorised data was disclosed. Internal exceptions and confidential values do not belong unfiltered in error messages or extension fields. GraphQL Specification, Response, GraphQL: Security.
The core specification prescribes no particular transport. The supplementary GraphQL-over-HTTP text is a Stage 2 draft at the source date. Treat its media-type and status recommendations accordingly and check what your server and clients actually support. A draft does not prove every existing API must already follow exactly the same HTTP contract.
With browser cookies, CORS and CSRF protection remain additionally relevant. Even a single GraphQL endpoint does not bypass these browser conditions.
Introduce limits with traceable negative tests of your own
In an isolated environment, test a legitimate operation and forbidden object or field access with your own test identities. Compare the same data access through alternative resolver paths. For partial errors, inspect remaining data; for mutations, inspect actual state.
Small controlled boundary cases initially suffice for resource rules: an allowed list quantity and one just above it, an allowed operation and one just over the cost budget. Observe downstream work too. This is targeted functional testing, not general load or availability evidence.
Plan rollback that restores valid clients while preserving an effective boundary, such as a previously tested budget configuration or blocking one problematic operation. Setting every limit to ‘unlimited’ or removing field permissions would not be a safe fix. The API Security Check performs no GraphQL queries, resolver or cost tests. For your evidence, continue with testing your own API security; reading HTTP findings interprets public response signals.
Sources and date
Sources checked: 27 September 2026. Links also appear alongside the statements they support.