---
title: "Allow only known queries from your own · Agent Wranglers"
url: https://agentwranglers.se/playbook/api-design/graphql-and-grpc/graphql-trusted-documents
id: API-86
level: Optional
updated: 2026-10-22
tokens: 913
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Allow only known queries from your own apps

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [GraphQL and gRPC](https://agentwranglers.se/playbook/api-design/graphql-and-grpc.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers.md). Updated 2026-10-22.

- ID: API-86
- Level: Optional
- Chapter: [GraphQL and gRPC](https://agentwranglers.se/playbook/api-design/graphql-and-grpc.md)
- Rule: When only your own apps call a GraphQL API, register each query at build time. The server runs only registered queries, sent by their hash.
- Check: Send a valid query that is not on the list to production. It is refused. Every query in the client's build output has a hash in the server's list.
- How: Registered queries, also called trusted documents, turn an open query language into a fixed list. A stranger then cannot send a costly or probing query at all. The client sends a hash, which also shortens requests and lets GET answers be cached. Keep the open endpoint for public or partner APIs, with the limits from [lock down GraphQL endpoints](https://agentwranglers.se/playbook/adversarial-testing/abuse-and-limits/graphql-endpoints.md). Old app versions still send old hashes, so keep each registered query as long as [keep old app versions working against the API](https://agentwranglers.se/playbook/mobile-apps/updates-and-versions/old-versions-keep-working.md) asks.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### JavaScript example

```js
const trusted = new Map(Object.entries(JSON.parse(readFileSync('persisted-queries.json', 'utf8'))));

app.post('/graphql', express.json(), (req, res, next) => {
  const query = trusted.get(req.body?.extensions?.persistedQuery?.sha256Hash);
  if (!query) return res.status(400).json({ errors: [{ message: 'Unknown query', extensions: { code: 'PERSISTED_QUERY_NOT_FOUND' } }] });
  req.body.query = query;
  next();
});
```

### Python example

```python
TRUSTED = json.loads(Path("persisted-queries.json").read_text())

def resolve_query(body: dict) -> str:
    query_hash = body.get("extensions", {}).get("persistedQuery", {}).get("sha256Hash")
    if query_hash not in TRUSTED:
        raise UnknownQuery(query_hash)
    return TRUSTED[query_hash]
```

## Sources

- [GraphQL, Performance](https://graphql.org/learn/performance/)

## Related rules

- SEC-102 [Lock down GraphQL endpoints](https://agentwranglers.se/playbook/adversarial-testing/abuse-and-limits/graphql-endpoints.md) in Adversarial testing
- MOB-13 [Keep old app versions working against the API](https://agentwranglers.se/playbook/mobile-apps/updates-and-versions/old-versions-keep-working.md) in Mobile apps

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/api-design/graphql-and-grpc/graphql-trusted-documents">Agent Wranglers API design playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Give every GraphQL error a stable code](https://agentwranglers.se/playbook/api-design/graphql-and-grpc/graphql-error-codes.md)
- Next: [Never reuse or change a protobuf field number](https://agentwranglers.se/playbook/api-design/graphql-and-grpc/protobuf-field-numbers.md)
- [GraphQL and gRPC](https://agentwranglers.se/playbook/api-design/graphql-and-grpc.md)
