# Configure GraphQL Proxying

API7 Gateway can proxy GraphQL traffic like any other HTTP API. In environments where the GraphQL-aware plugins are available, you can also apply GraphQL-specific rate limiting and caching policies.

## Prerequisites[​](#prerequisites "Direct link to Prerequisites")

* An API7 Enterprise instance is running.
* A Gateway Group is created and a Gateway instance is running.
* A [token from the Dashboard](https://docs.api7.ai/api7-gateway/reference/obtain-dashboard-token.md).

## Proxy GraphQL Requests[​](#proxy-graphql-requests "Direct link to Proxy GraphQL Requests")

Start with a standard HTTP route that forwards GraphQL requests to the upstream.

The following examples use `https://countries.trevorblades.com/` as an upstream. When proxying to an HTTPS upstream that expects its own hostname, set `scheme: https` and `pass_host: node`.

* ADC
* Admin API

adc.yaml

```
services:

  - name: graphql-service

    upstream:

      scheme: https

      pass_host: node

      nodes:

        - host: countries.trevorblades.com

          port: 443

          weight: 1

    routes:

      - name: graphql-route

        uris:

          - /graphql

        methods:

          - POST

    labels:

      docs-example: graphql-service
```

Preview the changes owned by this example and confirm that the diff contains no unintended updates or deletions:

```
adc diff -f adc.yaml \

  --include-resource-type service \

  --label-selector docs-example=graphql-service
```

Synchronize the reviewed configuration:

```
adc sync -f adc.yaml \

  --include-resource-type service \

  --label-selector docs-example=graphql-service
```

```
# 1. Create a Service for the GraphQL upstream

curl -k "https://localhost:7443/apisix/admin/services/graphql-service?gateway_group_id={gateway_group_id}" -X PUT \

  -H "X-API-KEY: ${API_KEY}" \

  -H "Content-Type: application/json" \

  -d '{

    "name": "graphql-service",

    "upstream": {

      "type": "roundrobin",

      "scheme": "https",

      "pass_host": "node",

      "nodes": [

        {

          "host": "countries.trevorblades.com",

          "port": 443,

          "weight": 1

        }

      ]

    }

  }'



# 2. Create a Route for the Service

curl -k "https://localhost:7443/apisix/admin/routes/graphql-route?gateway_group_id={gateway_group_id}" -X PUT \

  -H "X-API-KEY: ${API_KEY}" \

  -H "Content-Type: application/json" \

  -d '{

    "name": "graphql-route",

    "paths": ["/graphql"],

    "methods": ["POST"],

    "service_id": "graphql-service"

  }'
```

In the local validation environment, the basic Admin API example worked end to end after configuring the upstream with `scheme: https` and `pass_host: node`.

The following scenarios update this same service and route. Verify rate limiting before applying the caching scenario because the caching configuration replaces `graphql-limit-count` with `graphql-proxy-cache`.

## GraphQL Rate Limiting[​](#graphql-rate-limiting "Direct link to GraphQL Rate Limiting")

The [`graphql-limit-count`](https://docs.api7.ai/hub/graphql-limit-count.md) plugin provides GraphQL-aware rate limiting based on query depth.

### Configure GraphQL Rate Limiting[​](#configure-graphql-rate-limiting "Direct link to Configure GraphQL Rate Limiting")

Update the existing route to enable the `graphql-limit-count` plugin. This plugin only supports the `POST` method.

This plugin requires additional fields beyond `count`, `time_window`, `key_type`, and `key`. At minimum, include `policy`. For example, the local policy uses `policy: local`.

* Admin API
* ADC

```
curl -k "https://localhost:7443/apisix/admin/routes/graphql-route?gateway_group_id={gateway_group_id}" -X PUT \

  -H "X-API-KEY: ${API_KEY}" \

  -H "Content-Type: application/json" \

  -d '{

    "name": "graphql-route",

    "paths": ["/graphql"],

    "service_id": "graphql-service",

    "methods": ["POST"],

    "plugins": {

      "graphql-limit-count": {

        "count": 6,

        "time_window": 60,

        "rejected_code": 429,

        "policy": "local",

        "key_type": "var",

        "key": "remote_addr"

      }

    }

  }'
```

adc.yaml

```
services:

  - name: graphql-service

    upstream:

      scheme: https

      pass_host: node

      nodes:

        - host: countries.trevorblades.com

          port: 443

          weight: 1

    routes:

      - name: graphql-route

        uris:

          - /graphql

        methods:

          - POST

        plugins:

          graphql-limit-count:

            count: 6

            time_window: 60

            rejected_code: 429

            policy: local

            key_type: var

            key: remote_addr

    labels:

      docs-example: graphql-service
```

Preview the changes owned by this example and confirm that the diff contains no unintended updates or deletions:

```
adc diff -f adc.yaml \

  --include-resource-type service \

  --label-selector docs-example=graphql-service
```

Synchronize the reviewed configuration:

```
adc sync -f adc.yaml \

  --include-resource-type service \

  --label-selector docs-example=graphql-service
```

Repeat the following query four times. The first three requests should succeed and the fourth should return `429 Too Many Requests`:

```
for i in $(seq 1 4); do

  curl -s -o /dev/null -w '%{http_code}\n' "http://localhost:9080/graphql" \

    -H "Content-Type: application/json" \

    -d '{"query":"query { continents { code } }"}'

done
```

## GraphQL Caching[​](#graphql-caching "Direct link to GraphQL Caching")

The [`graphql-proxy-cache`](https://docs.api7.ai/hub/graphql-proxy-cache.md) plugin provides GraphQL-aware caching for query operations while bypassing mutations.

### Configure GraphQL Caching[​](#configure-graphql-caching "Direct link to Configure GraphQL Caching")

Update the same route to replace the rate-limiting configuration with `graphql-proxy-cache`.

In the local validation environment, each GraphQL-aware plugin configuration was validated independently. `graphql-limit-count` returned `429` after the configured quota was exceeded, and `graphql-proxy-cache` returned `Apisix-Cache-Status: MISS` on the first request and `Apisix-Cache-Status: HIT` on the second identical request.

* Admin API
* ADC

```
curl -k "https://localhost:7443/apisix/admin/routes/graphql-route?gateway_group_id={gateway_group_id}" -X PUT \

  -H "X-API-KEY: ${API_KEY}" \

  -H "Content-Type: application/json" \

  -d '{

    "name": "graphql-route",

    "paths": ["/graphql"],

    "service_id": "graphql-service",

    "methods": ["POST"],

    "plugins": {

      "graphql-proxy-cache": {}

    }

  }'
```

adc.yaml

```
services:

  - name: graphql-service

    upstream:

      scheme: https

      pass_host: node

      nodes:

        - host: countries.trevorblades.com

          port: 443

          weight: 1

    routes:

      - name: graphql-route

        uris:

          - /graphql

        methods:

          - POST

        plugins:

          graphql-proxy-cache: {}

    labels:

      docs-example: graphql-service
```

Preview the changes owned by this example and confirm that the diff contains no unintended updates or deletions:

```
adc diff -f adc.yaml \

  --include-resource-type service \

  --label-selector docs-example=graphql-service
```

Synchronize the reviewed configuration:

```
adc sync -f adc.yaml \

  --include-resource-type service \

  --label-selector docs-example=graphql-service
```

### Purge Cache[​](#purge-cache "Direct link to Purge Cache")

You can purge the cache for a specific GraphQL query by sending a `DELETE` request to the plugin's control endpoint.

```
curl -X DELETE "http://localhost:9080/apisix/plugin/graphql-proxy-cache/*"
```

## Validate the Configuration[​](#validate-the-configuration "Direct link to Validate the Configuration")

You can test the basic GraphQL proxying by sending a `POST` request with a GraphQL query.

```
curl -X POST "http://localhost:9080/graphql" \

  -H "Content-Type: application/json" \

  -d '{"query": "query { countries { code } }"}'
```

The gateway proxies the GraphQL request and returns the response from the upstream:

```
{"data":{"countries":[{"code":"AD"},{"code":"AE"}]}}
```

If `graphql-proxy-cache` is enabled, repeat the same query twice and confirm the cache status headers:

```
curl -i -X POST "http://localhost:9080/graphql" \

  -H "Content-Type: application/json" \

  -d '{"query":"query { continents { code } }"}'



curl -i -X POST "http://localhost:9080/graphql" \

  -H "Content-Type: application/json" \

  -d '{"query":"query { continents { code } }"}'
```

The first response should include `Apisix-Cache-Status: MISS` and the second should include `Apisix-Cache-Status: HIT`.

## Next Steps[​](#next-steps "Direct link to Next Steps")

* [Configure Rate Limiting](https://docs.api7.ai/api7-gateway/how-to-guides/traffic-management/rate-limiting.md) — explore more advanced rate limiting strategies.
* [Configure Proxy Caching](https://docs.api7.ai/api7-gateway/how-to-guides/traffic-management/proxy-cache.md) — learn about standard HTTP caching.
* [Configure gRPC Proxying](https://docs.api7.ai/api7-gateway/how-to-guides/protocol-proxy/grpc-proxy.md) — handle gRPC services and transcoding.
* [Configure WebSocket Proxying](https://docs.api7.ai/api7-gateway/how-to-guides/protocol-proxy/websocket-proxy.md) — manage real-time, bidirectional communication.
