---
title: "GraphQL Gateway: Policies, Analytics & MCP for GraphQL"
description:
  "Put your GraphQL endpoint behind Zuplo. Depth and complexity limits,
  introspection control, operation-aware analytics, a one-line GraphQL-to-MCP
  server, and a schema-aware playground, on the same gateway as your REST APIs."
canonicalUrl: "https://zuplo.com/features/graphql"
sourceUrl: "https://zuplo.com/features/graphql"
pageType: "feature"
generatedAt: "2026-08-18"
---

# Your GraphQL endpoint, behind a real gateway

> GraphQL hides behind one POST route, where most gateways go blind. Zuplo sees
> every operation: complexity limits, per-operation analytics, and a built-in
> MCP server.

[Start for Free](https://portal.zuplo.com/signup?utm_source=zuplo&utm_medium=web&utm_campaign=feature_graphql&utm_content=hero)
· [Read the Docs](https://zuplo.com/docs/articles/graphql)

## An HTTP gateway can't see a GraphQL API

REST gives a gateway many routes and methods to reason about. GraphQL collapses
everything into one POST endpoint, so a gateway built for HTTP is blind to the
operations underneath. Security, analytics, and limits stop at the door.

- **A second gateway, just for GraphQL** — Your REST traffic runs through one
  gateway. GraphQL gets a separate bespoke proxy with its own auth, limits, and
  dashboards. Two systems to secure and operate.
- **One endpoint, zero visibility** — Everything is POST /graphql, so your HTTP
  analytics see one route doing all the work. No idea which query is slow or
  which mutation is failing.
- **Expensive queries reach your resolvers** — A deeply nested query or a
  complexity bomb sails straight through to your origin. By the time your
  resolvers fall over, the request is already inside.
- **Introspection open in production** — The introspection that powers your
  playground is a full schema map for an attacker. Left on in prod, it exposes
  every type, field, and mutation you have.

## GraphQL as a first-class citizen of your gateway

- **One gateway, REST and GraphQL** — Proxy your GraphQL upstream through a POST
  route and every Zuplo policy applies natively: auth, rate limiting, logging,
  custom TypeScript. No second gateway, no separate control plane.
- **Every operation is first-class** — Zuplo parses each request into named
  operations. Analytics, limits, and logs work per query and per mutation, not
  flattened into a single endpoint.
- **Block the expensive query at the edge** — Depth limiting, complexity
  analysis, and introspection control run before the request reaches your
  resolvers, across 300+ data centers. Your origin never feels it.

## Stop the expensive query before your resolvers do

A GraphQL route is just a POST route with a stack of policies. Depth and
complexity analysis reject abusive queries at the edge; introspection controls
keep your schema private. The expensive query never reaches your upstream.

**Proxy your GraphQL upstream:**

```json
{
  "/graphql": {
    "post": {
      "operationId": "graphql",
      "x-zuplo-route": {
        "handler": {
          "export": "urlRewriteHandler",
          "module": "$import(@zuplo/runtime)",
          "options": {
            "baseUrl": "${env.GRAPHQL_UPSTREAM}/graphql"
          }
        },
        "policies": {
          "inbound": [
            "api-key-inbound",
            "graphql-complexity-limit",
            "graphql-disable-introspection"
          ]
        }
      }
    }
  }
}
```

**Limit query depth and complexity:**

```json
{
  "name": "graphql-complexity-limit",
  "policyType": "graphql-complexity-limit-inbound",
  "handler": {
    "export": "GraphQLComplexityLimitInboundPolicy",
    "module": "$import(@zuplo/runtime)",
    "options": {
      "maxDepth": 7,
      "maxComplexity": 1000
    }
  }
}

// Over the limit? Rejected at the edge,
// before your GraphQL server parses it.
```

Policy building blocks available:

- Query depth limiting
- Complexity / cost analysis
- Disable public introspection
- Filter introspection responses
- Per-consumer rate limiting
- Any auth: API key, JWT, OAuth

## See every operation, not one busy endpoint

A dedicated GraphQL section with metrics that understand operations, not just
HTTP status codes. Find the slow query, the failing mutation, and whether
latency lives in your resolvers or your policies.

Example metrics shown on the analytics dashboard:

- **Operations**: 1.24M (this window)
- **Success rate**: 99.2% (error / success split)
- **p95 latency**: 84ms (resolver p95 31ms)
- **Error classes**: 3 (resolver · validation · auth)

Operation types breakdown: Query 72%, Mutation 22%, Subscription 6%.

Example per-operation table:

| Operation            | Type     | Volume | Err rate | Complexity | p95   |
| -------------------- | -------- | ------ | -------- | ---------- | ----- |
| GetCharacters        | query    | 612K   | 0.1%     | 240        | 62ms  |
| GetCharacterEpisodes | query    | 318K   | 0.4%     | 910        | 148ms |
| CreateReview         | mutation | 94K    | 2.1%     | 120        | 203ms |
| SearchLocations      | query    | 71K    | 0.0%     | 180        | 44ms  |

[Read the Analytics Docs](https://zuplo.com/docs/analytics/tabs/graphql)

## Turn your GraphQL API into an MCP server

GraphQL is a query language, so an LLM needs the schema before it can ask
anything useful. Flag a route as GraphQL MCP and Zuplo generates two tools
automatically: one to fetch the schema, one to run queries.

- **Introspection tool** — Fetches the schema so the agent knows what to ask.
- **Execute tool** — Runs queries through the gateway and your policies.

Both tools are generated automatically, with no hand-written definitions.

How it works:

1. **Agent calls introspection** — It pulls your schema and learns which
   queries, mutations, and types exist.
2. **Agent builds a valid query** — Armed with the schema, the LLM constructs a
   query that asks exactly what it needs.
3. **Execute runs it through the gateway** — The query runs behind your auth,
   rate limits, and complexity policies, like any client.

[Read the GraphQL MCP Docs](https://zuplo.com/docs/mcp-server/graphql)

## GraphQL support, not a GraphQL bolt-on

- **Operation-level security** — Complexity and depth limits reject abusive
  queries at the edge, and introspection controls keep your schema private in
  production. Garbage never reaches your server.
- **Operation-aware analytics** — See operations, success rate,
  total-vs-resolver p95, and errors split into resolver, validation, and auth. A
  per-operation table ranks every query by volume, complexity, and latency.
- **GraphQL to MCP in one route** — Flag a GraphQL route for MCP and Zuplo
  generates an introspection tool and an execute tool automatically. Agents
  discover your schema and run queries, behind your auth.
- **Schema-aware docs and playground** — Your developer portal renders a
  browsable type reference and a live query playground straight from your
  schema. No separate GraphiQL to host or sync.

## What teams use this for

**"We run REST and GraphQL and don't want two gateways to operate."** Add a POST
/graphql route pointing at your GraphQL server, alongside your REST routes. The
same auth, rate limiting, logging, and GitOps deploys cover both. One gateway,
one repo.

**"Someone is sending 12-level-deep queries that melt resolvers."** Add the
complexity-limit policy and set depth and cost thresholds. Anything over the
limit is rejected at the edge before it reaches your upstream. Pair it with
per-consumer rate limiting.

**"We need introspection for our MCP server but not the public endpoint."** Keep
introspection where the MCP server needs it and disable it on the public route,
or filter it to expose only the types you want public. The schema map stays
internal.

**"Latency is up but we can't tell if it's our server or the gateway."** Compare
total p95 against resolver p95 in the GraphQL analytics tab. Low resolver but
high total means the time is in parsing, validation, or auth at the gateway. The
per-operation table shows which query regressed.

## FAQ

Common questions about running GraphQL through Zuplo.

**Can I put a GraphQL API behind an API gateway?** Yes. With Zuplo you create a
POST /graphql route with the URL Rewrite handler pointing at your GraphQL
server, and every policy you already use for REST — authentication, rate
limiting, logging, custom TypeScript — applies to GraphQL traffic too. Zuplo
tags the route with the x-graphql extension automatically when you pick "GraphQL
Endpoint", so the gateway knows to parse operations and surface GraphQL-specific
analytics. You get one gateway and one control plane for both REST and GraphQL
instead of running a second bespoke proxy.

**How do I stop expensive or malicious GraphQL queries?** GraphQL's flexibility
is also its attack surface — a deeply nested query or a complexity bomb can
knock over your resolvers. Zuplo enforces this at the edge before the request
reaches your upstream: the graphql-complexity-limit-inbound policy does query
cost analysis and depth limiting, and you can reject anything over your
thresholds. Combined with edge rate limiting per consumer, the expensive query
never touches your GraphQL server.

**How do I disable GraphQL introspection in production?** Introspection is great
for a playground and dangerous in production — it hands an attacker a full map
of your types, fields, and mutations. Zuplo gives you two policies:
graphql-disable-introspection-inbound blocks introspection on public routes
entirely, and graphql-introspection-filter-outbound filters which types and
fields appear in introspection responses so you can hide internal schema while
keeping the rest browsable.

**Can I get per-operation analytics for a GraphQL API?** Yes — this is the part
most gateways miss. To an HTTP-only gateway, a GraphQL API is a single POST
/graphql route, so you can't tell which query is slow or which mutation is
failing. Zuplo's graphql-analytics-outbound policy parses each request into
named operations and surfaces operation-aware metrics: total operations, success
rate, total vs resolver p95 latency, and errors split into resolver, validation,
and auth classes — plus a searchable per-operation table with volume,
complexity, and latency for every query shape.

**How do I turn a GraphQL API into an MCP server for AI agents?** Flag your
GraphQL route as an MCP GraphQL tool and Zuplo automatically generates two MCP
tools: an introspection tool that fetches the schema so the LLM understands what
queries exist, and an execute tool that runs queries against the endpoint. AI
agents discover and query your GraphQL API with no hand-written tool
definitions, and any auth or rate limiting on the route carries forward to the
MCP server automatically.

**How do I publish interactive GraphQL docs for my consumers?** Register the
@zudoku/plugin-graphql plugin in your Zuplo Developer Portal configuration and
point it at a live endpoint or a local schema file. Consumers get a schema-aware
type reference they can browse and an interactive playground where they run real
queries from the docs — no separate GraphiQL deployment to host or keep in sync.

**What's the difference between resolver latency and total latency?** Total
latency is the full time Zuplo spends on an operation; resolver latency is just
the time your upstream GraphQL server spends resolving it. Zuplo charts both
side by side. If total p95 is high but resolver p95 is low, the time is going to
parsing, validation, complexity analysis, or auth in the gateway — not your
backend. If both are high, the bottleneck is upstream. It turns a vague "GraphQL
is slow" into a precise place to look.

**Does GraphQL support work with the rest of the Zuplo platform?** Yes — a
GraphQL endpoint is just a Zuplo route, so everything composes. The same
authentication (API keys, JWT, OAuth), rate limiting, monetization, caching,
custom code, GitOps deploys, and unlimited environments you use for REST routes
work on GraphQL routes too. You manage one gateway, defined as files in your Git
repo, for every protocol you ship.

## Next steps

- Start a free Zuplo project and point a POST /graphql route at your endpoint:
  [Start for Free](https://portal.zuplo.com/signup?utm_source=zuplo&utm_medium=web&utm_campaign=feature_graphql&utm_content=footer)
- Read the GraphQL docs:
  [Read the Docs](https://zuplo.com/docs/articles/graphql)
