Skip to content

Security & Performance

Every lesson so far has celebrated GraphQL’s flexibility: one endpoint, a typed graph, and a client that asks for exactly the fields it wants. That same flexibility is the reason GraphQL needs its own security and performance playbook. The very feature that delights front-end teams — “ask for anything you can reach” — is also an open invitation to anyone who wants to ask for too much.

A REST API spreads its surface across many fixed URLs, and each one tends to do a small, predictable amount of work. A GraphQL API collapses all of that into a single endpoint that accepts arbitrary, client-authored queries. The power and the risk are two sides of the same coin:

  • Power — a client composes precisely the response shape it needs in one round trip, traversing relationships the server never had to anticipate.
  • Risk — a client (or an attacker) can compose a query the server also never anticipated: deeply nested, wildly aliased, or fanning out into thousands of database calls from a single innocent-looking request.

Because the server cannot know in advance which queries it will receive, it must reason about cost, identity, and trust per request, dynamically. That is the whole job of this module.

Securing and tuning a GraphQL API comes down to four recurring questions. Keep them in mind and the rest of the module is just detail:

  1. Who is asking? — establish identity once per request. This is authentication, and it belongs in the request context, not in the schema.
  2. Are they allowed? — decide, per field and per type, whether this identity may see this data. This is authorization, and it lives in resolvers, directives, or middleware.
  3. How expensive is this query? — a single request can be cheap or catastrophic. Depth limiting and complexity (cost) analysis reject queries that would cost too much before they run.
  4. Have we seen this before?persisted queries, caching, and rate limiting turn repeated work into cheap, predictable work and shrink the attack surface.
flowchart LR
  Q["Client query"] --> A["Authenticate: who is asking? (context)"]
  A --> Z["Authorize: are they allowed? (resolvers)"]
  Z --> C["Cost check: depth & complexity limits"]
  C --> X["Execute resolvers"]
  X --> D["Data sources"]
  X --> R["Response (cacheable, rate-limited)"]
  C -.->|"too deep / too costly"| Rej["Reject before execution"]
A request passes through identity, permission, and cost checks before resolvers ever touch the data.

This is the security and performance module of the course. By the end of it you will be able to lock a GraphQL API down without sacrificing the ergonomics that made you reach for GraphQL in the first place. The five lessons are:

  1. Security & Performance (you are here) — the threat-and-cost surface of a single flexible endpoint.
  2. Authentication & Context — resolving the current user once per request from a token, and why the context is the right home for it.
  3. Authorization — field- and type-level permission checks, and the choice between returning null and throwing a forbidden error.
  4. Depth & Complexity — rejecting deep, aliased, or expensive queries with depth limits, cost analysis, and timeouts.
  5. Persisted Queries & Caching — allowlisting operations, automatic persisted queries, response and per-field caching, and rate limiting.

Throughout, the in-browser runner executes the security-relevant logic for real — a resolver reading an authenticated context, a field that refuses an unauthorized user, and a function that measures the depth of a parsed query — so you can see the mechanisms work rather than take them on faith.

Why does a single GraphQL endpoint need its own security playbook compared with a REST API?
Which four questions summarize securing and tuning a GraphQL API?
In one sentence, what makes GraphQL’s flexibility a risk?