Which controls fit depends on who writes the queries. If your own apps are the sole callers, lock operations with trusted documents and turn introspection off in production. If outside developers write queries, you cannot allowlist them, so rely on limits, cost controls, authentication and strict authorization instead.
The key question
Ask one thing first: do you know every query that will be sent? The GraphQL security guide ties several recommendations to that. Trusted documents suit APIs that serve first party clients, and nobody else. Operations are written in development, submitted to the server's allowlist at build time with an ID, and in production the client sends the ID alone. The guide notes this cannot work for public APIs, because third party operations are not known in advance.
The same guide says introspection can be forbidden in non development environments for APIs serving first party clients, and cautions that disabling it is a form of security through obscurity and not enough alone.
Comparison
| Control | First party API | Public API |
|---|---|---|
| Trusted documents allowlist | Strongly suited | Not possible |
| Introspection | Off in production | Often on by design, consider restricting to approved callers |
| Depth limit | Yes | Yes |
| Breadth, alias and batch limits | Yes | Yes |
| Pagination caps | Yes | Yes |
| Query cost analysis | Optional | Often needed |
| Rate limiting in business logic | Yes | Yes, tuned per client |
| Field and object authorization | Required | Required |
| Error masking | Yes | Yes |
| Authentication | Required | Required, per client keys |
Demand control for a public API
With an open set of queries, the guide lists the layered options: paginated list fields, depth limiting, breadth and batch limiting, rate limiting and query complexity analysis. It advises a separate, smaller limit for how deeply lists can be nested, since nested lists can multiply the data returned. It also says rate limits are generally better applied in the business logic layer, because a count of network requests misses the cost of each query.
For cases where a legitimate client needs a deep query and a low blanket limit is impractical, the guide suggests relying on rate limiting or complexity analysis. See the earlier post on choosing between depth, amount and cost limits.
A baseline for each case
First party API
- Authenticate every request.
- Extract and publish trusted documents at build time.
- Reject unknown operations in production.
- Disable introspection and mask errors.
- Keep depth, pagination and batch limits anyway.
- Authorize every field and object.
Public API
- Issue per client credentials and apply per client limits.
- Apply depth, breadth, alias and batch limits.
- Cap page sizes.
- Add cost analysis if some fields are expensive.
- Consider restricting introspection to approved callers, and publish a schema document instead.
- Mask errors and field suggestions.
- Authorize every field and object.
What stays the same
Whoever calls the API, authorization does not change. The GraphQL project recommends putting authorization logic in the business logic layer, validating and sanitizing argument values, and keeping a single source of truth. An allowlist reduces the query shapes an attacker can use, but each allowed query still needs proper access checks.
Try it yourself
Click through it at your own pace, or open it full screen.
Look at which controls an API scan reports on and compare them with the baseline above.
Quick check
Can a public GraphQL API use trusted documents?
No. The GraphQL security guide says outside clients write operations you cannot know ahead of time. Use limits, cost analysis and authorization.
Is disabling introspection enough for a first party API?
No. It helps, but the guide says an allowlist and authorization give stronger protection.
Safeguard scans your dependencies and SBOMs for known vulnerabilities, including GraphQL server libraries, and shows which are reachable. A free scan is at gold.safeguard.sh/scan.
Sources
Last reviewed: 19 October 2026.