01. Two Layers One Edge
Imagine a building with a security desk at the main entrance that checks everyone’s ID, limits how many times they can come in, and watches for troublemakers—but once inside, each room has its own rulebook about which items you can actually touch. That’s the two-layer edge for APIs: one system guards the front door, and a separate system governs what happens inside each room. This setup is for managing APIs at both the transport edge (who gets in, how often) and the graph level (what exactly they’re allowed to ask for).
The front-door system—an API management platform—handles authentication (checking badges), consumer credentials (partner IDs), quotas and rate limits (how many visits per hour), spike arrest (slowing a sudden rush), web application firewall rules, and IP policies (blocking certain addresses). It works across every API the company exposes, whether REST, SOAP, or GraphQL. But to that front desk, GraphQL traffic looks like a single opaque POST request—like a visitor who enters carrying a sealed box. The front desk cannot see inside the box, so it cannot rate-limit an expensive query differently from a cheap one, cannot tell which fields a consumer uses, and cannot detect if the schema changed in a breaking way.
The deeper layer—the GraphQL platform—adds the room-specific rules: per-field authorization declared as schema directives, depth and complexity limits so a single query can’t ask for too much, and persisted-operation allow-lists that only run pre-approved requests. This layer also includes schema registry checks and per-field usage reporting. Without it, the front door would let in a wildly expensive query that asks for entire database dumps, and the system would crash or slow to a crawl—because the security desk had no way to know the box was full of lead weights.
API management platforms and GraphQL platforms are two complementary governance layers, not competitors. The management product governs the transport edge of the entire API estate. It includes authentication, consumer credentials, quotas, and rate limits. It also applies spike arrest, web application firewall rules, and IP policies.
But to a generic API management tool, GraphQL is one opaque post endpoint. These endpoint-level tools cannot rate-limit an expensive query differently from a cheap one. They cannot tell which fields a consumer uses. They cannot detect a breaking schema change. So treating the graph as just another API yields exactly one governed route and zero graph governance.
The GraphQL platform completes the picture. It includes a router and schema registry. It offers persisted operation allow-lists, which restore cacheability and shrink the attack surface. It applies depth and complexity limits. It provides per-field telemetry and usage-aware breaking-change gates. Field-level authorization inside the graph is declared as schema directives and enforced at execution.
The split exists because a gateway sees only the transport endpoint. The management layer controls the front door. The router and registry own the graph. Buying one and assuming it covers the other is the failure mode to warn against.
Configuring a resolver to map a GraphQL field to a backend HTTP API
<http-data-source>
<http-request>
<set-method>GET</set-method>
<set-url>https://myapi.contoso.com/api/users</set-url>
</http-request>
</http-data-source>
The subsystem described in "Two Layers One Edge" operates as a sequential two-stage governance pipeline. First, the API management platform handles transport—authentication and edge security, consumer management with credentials and quotas, cross-API policy (logging, throttling, header manipulation), and consumer-level analytics. It validates the token at the front door, enforces spike arrest and Web Application Firewall rules, and forwards claims to the inner layer. Second, the GraphQL platform (router plus schema registry) takes over: it applies persisted-operation allow-lists to restrict execution to registered documents, enforces depth/complexity/cost limits per query shape, runs schema registry checks and usage reporting per field per client, and executes field-level authorization declared as schema directives. If a request fails at the first layer (e.g., invalid token), it is rejected before reaching the graph; if it passes but violates a graph-layer constraint (e.g., unregistered operation), it is rejected at the router.
The design preserves the invariant that transport governance and graph governance are complementary, non-overlapping layers: the management product owns the endpoint-level security and consumer onboarding, while the GraphQL platform owns schema-aware, per-query control. This separation ensures that each layer handles only what it can express—the gateway cannot see or govern the shape of GraphQL queries, and the graph router cannot issue API keys or enforce rate limits per consumer. The failure mode the designers explicitly warn against is "buying one and assuming it covers the other." The alternative rejected is attempting to implement both responsibilities in a single product, which would require the API management tool to understand GraphQL semantics (field selection, query complexity, schema evolution) or the GraphQL platform to manage consumer credentials and transport policies. The cost avoided by this choice is the inability to enforce per-query cost and schema-aware governance—an endpoint-level tool "cannot rate-limit an expensive query differently from a cheap one, cannot tell which fields a consumer uses, cannot detect a breaking schema change." By keeping layers separate, each can do its job without needing to replicate the other's domain.
A concrete failure mode emerges when an enterprise deploys only the API management layer and neglects the GraphQL platform. An operator would see a single expensive query—say one requesting thousands of nested fields—overwhelming the backend. The API management console logs only one POST to /graphql, shows a high total call count and latency, but provides no per-field or per-operation breakdown. There is no alert for "breaking schema change" because none exists; the operator might instead observe a sudden surge of client errors after a schema modification, with no trace linking the errors to a field or consumer. The signal is a vague latency spike and increased error rate, with no way to distinguish an abusive query from a legitimate one. The only recourse is to manually inspect GraphQL logs outside the management system, because "endpoint-level tools therefore cannot rate-limit an expensive query differently from a cheap one."
Expensive query abuse — no query‑level cost limit
- Trigger – A consumer sends a deeply nested or highly complex GraphQL query that demands disproportionate backend resources.
- Guard – None. The source explicitly states that endpoint‑level tools “cannot rate‑limit an expensive query differently from a cheap one”. API Management’s spike arrest and rate‑limits operate on call count, not query shape.
- Posture – Fail‑soft. The query is executed, degrading backend performance and potentially overwhelming the data source.
- Operator signal – No direct metric; the only observable symptom is elevated backend latency or error rates that cannot be attributed to any single consumer or operation.
- Recovery – Manual. The operator must deploy a graph‑aware router (referenced as “complexity limits” in the companion text) or migrate to a synthetic GraphQL API with custom resolvers that enforce cost thresholds.
Breaking schema change — consumer queries silently fail
- Trigger – The backend GraphQL schema evolves (e.g., a field is removed or renamed) without a coordinated roll‑out.
- Guard – None. The source says APIM “cannot detect a breaking schema change”. The schema imported into the management plane is static until re‑imported.
- Posture – Fail‑soft. Queries that reference the removed field return errors, but other queries continue to work.
- Operator signal – Consumer‑side error reports (e.g.,
Cannot query field); no alert is raised in API Management. - Recovery – Manual. The operator must re‑import the updated schema and, if using a pass‑through API, adjust backend or rollback the schema change. A graph‑aware registry would provide “usage‑aware breaking‑change gates”.
Missing field‑level usage visibility — blind optimization
- Trigger – Operations need to know which fields are accessed by which consumers to plan deprecations or optimizations.
- Guard – None. The source states APIM “cannot tell which fields a consumer uses”. The portal provides “consumer‑level analytics” only at the transport level.
- Posture – Fail‑soft. The system continues to serve queries, but deprecation decisions are made without per‑field telemetry.
- Operator signal – Silent absence; no field usage report exists in API Management.
- Recovery – Manual. The operator must instrument a separate graph‑aware layer (schema registry) that supplies “per‑field, per‑client telemetry”.
Inefficient caching due to opaque POST endpoint
- Trigger – Repeated identical GraphQL queries are sent by different clients, all going to the same backend.
- Guard – None. The source explains that APIM “cannot cache by URL” because all queries arrive as one POST with varying bodies. REST‑style caching policies (e.g.,
cache-store,cache-lookup) are ineffective. - Posture – Fail‑soft. Every query still reaches the backend; no cached response is served, increasing latency and load.
- Operator signal – High backend response times and repeated backend calls for identical data; no cache‑hit metric for GraphQL.
- Recovery – Manual. The operator must adopt persisted operations over GET (referenced as “persisted queries over GET”) to restore cacheability, typically via a graph router.
Field‑level authorization bypass — security exposure
- Trigger – A consumer requests a field they are not allowed to read, but the request reaches the backend because API Management enforces only transport‑level auth (e.g., JWT validation).
- Guard – None at the API Management layer. The source says “field‑level authorization … declared as schema directives, enforced at execution” belongs to the graph platform, not to APIM.
- Posture – Fail‑open. The query proceeds and the unauthorized field may be returned unless the backend itself enforces per‑field access.
- Operator signal – Possibly none; a security audit or consumer misuse would reveal the gap.
- Recovery – Manual. The operator must implement field‑level authorization inside a graph‑aware router or embed custom resolver policies that check claims against each field.
In Two Layers One Edge, what triggers Expensive query abuse — no query‑level cost limit — and how is it caught?
Show answer
A consumer sends a deeply nested or highly complex GraphQL query that demands disproportionate backend resources.
From the research: Retrieval practice / testing effect — Testing (quizzing) boosts classroom learning: A systematic and meta-analytic review (2021)
In Two Layers One Edge, what triggers Breaking schema change — consumer queries silently fail — and how is it caught?
Show answer
The backend GraphQL schema evolves (e.g., a field is removed or renamed) without a coordinated roll‑out.
From the research: Retrieval practice / testing effect — Testing (quizzing) boosts classroom learning: A systematic and meta-analytic review (2021)