zuplo 6.71.21 → 6.71.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/articles/api-key-api.mdx +2 -2
- package/docs/articles/graphql-caching.mdx +96 -0
- package/docs/articles/graphql-security.mdx +137 -126
- package/docs/articles/graphql-support.mdx +107 -0
- package/docs/articles/graphql.mdx +20 -0
- package/docs/articles/hosting-options.mdx +14 -13
- package/docs/dev-portal/zudoku/components/landing-page.mdx +7 -7
- package/docs/dev-portal/zudoku/components/link.mdx +5 -5
- package/docs/dev-portal/zudoku/configuration/footer.mdx +2 -2
- package/docs/dev-portal/zudoku/configuration/overview.md +1 -1
- package/docs/dev-portal/zudoku/guides/redirects.md +2 -2
- package/docs/guides/proxying-between-zuplo-gateways.mdx +2 -3
- package/docs/policies/_index.md +2 -1
- package/docs/policies/graphql-analytics-outbound/doc.md +10 -6
- package/docs/policies/graphql-analytics-outbound/schema.json +5 -4
- package/docs/policies/graphql-cache-inbound/doc.md +137 -0
- package/docs/policies/graphql-cache-inbound/intro.md +15 -0
- package/docs/policies/graphql-cache-inbound/schema.json +74 -0
- package/docs/self-hosted/overview.md +121 -34
- package/package.json +4 -4
|
@@ -84,9 +84,9 @@ inside Zuplo)
|
|
|
84
84
|
```bash
|
|
85
85
|
# Your Zuplo Account Name
|
|
86
86
|
export ACCOUNT_NAME=my-account
|
|
87
|
-
# Your bucket
|
|
87
|
+
# Your bucket name (Found in Services > API Key Service)
|
|
88
88
|
export BUCKET_NAME=my-bucket
|
|
89
|
-
# Your Zuplo API Key (Found in Settings > Zuplo API Keys)
|
|
89
|
+
# Your Zuplo API Key (Found in Account Settings > Zuplo API Keys)
|
|
90
90
|
export API_KEY=zpka_YOUR_API_KEY
|
|
91
91
|
```
|
|
92
92
|
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Cache GraphQL responses with Zuplo
|
|
3
|
+
sidebar_label: Cache responses
|
|
4
|
+
description:
|
|
5
|
+
Serve repeated GraphQL queries from the edge with the GraphQL Cache policy to
|
|
6
|
+
cut latency and reduce load on your origin.
|
|
7
|
+
tags:
|
|
8
|
+
- caching
|
|
9
|
+
- backends
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
GraphQL clients send queries as POST request bodies, so a normal CDN — which
|
|
13
|
+
keys on the URL — can't cache them. The
|
|
14
|
+
[`graphql-cache-inbound`](../policies/graphql-cache-inbound.mdx) policy solves
|
|
15
|
+
this: it parses each query, normalizes it into a canonical form, and caches the
|
|
16
|
+
response in a [ZoneCache](../programmable-api/zone-cache.mdx) at the edge. Two
|
|
17
|
+
requests that are semantically identical share a cache entry even when their
|
|
18
|
+
bodies differ byte-for-byte.
|
|
19
|
+
|
|
20
|
+
Only `query` operations are cached. Mutations, subscriptions, malformed
|
|
21
|
+
documents, and responses that contain GraphQL `errors` are always forwarded to
|
|
22
|
+
your origin untouched.
|
|
23
|
+
|
|
24
|
+
## Add the cache policy
|
|
25
|
+
|
|
26
|
+
Add `graphql-cache-inbound` to the inbound policies of your GraphQL route.
|
|
27
|
+
|
|
28
|
+
<Stepper>
|
|
29
|
+
|
|
30
|
+
1. **Open `policies.json`**
|
|
31
|
+
|
|
32
|
+
In the Zuplo Portal, open the **Code** tab and add the policy definition:
|
|
33
|
+
|
|
34
|
+
```json title="config/policies.json"
|
|
35
|
+
{
|
|
36
|
+
"name": "graphql-cache",
|
|
37
|
+
"policyType": "graphql-cache-inbound",
|
|
38
|
+
"handler": {
|
|
39
|
+
"export": "GraphQLCacheInboundPolicy",
|
|
40
|
+
"module": "$import(@zuplo/graphql)",
|
|
41
|
+
"options": {
|
|
42
|
+
"cacheName": "graphql-responses",
|
|
43
|
+
"ttlSeconds": 60
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
2. **Attach it to your GraphQL route**
|
|
50
|
+
|
|
51
|
+
Add `"graphql-cache"` to the route's inbound policies in `routes.oas.json`,
|
|
52
|
+
ahead of any policy that rewrites the request.
|
|
53
|
+
|
|
54
|
+
3. **Verify the cache is working**
|
|
55
|
+
|
|
56
|
+
Send the same query twice and inspect the response headers. The policy adds
|
|
57
|
+
`x-cache: MISS` on the first request and `x-cache: HIT` on the second. The
|
|
58
|
+
`x-cache-key` header (first 8 characters of the cache key) confirms two
|
|
59
|
+
requests resolve to the same entry.
|
|
60
|
+
|
|
61
|
+
</Stepper>
|
|
62
|
+
|
|
63
|
+
## Cache authenticated traffic safely
|
|
64
|
+
|
|
65
|
+
By default, requests that carry an `authorization` or `cookie` header are
|
|
66
|
+
**not** cached — otherwise the first user's response would be served to
|
|
67
|
+
everyone. To cache per-user, list the headers that make a response user-specific
|
|
68
|
+
in `cacheKeyHeaders`. Each distinct value gets its own entry:
|
|
69
|
+
|
|
70
|
+
```json title="config/policies.json"
|
|
71
|
+
{
|
|
72
|
+
"options": {
|
|
73
|
+
"ttlSeconds": 30,
|
|
74
|
+
"cacheKeyHeaders": ["authorization"]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
:::caution
|
|
80
|
+
|
|
81
|
+
Setting `cacheKeyHeaders` to an empty array `[]` caches a single response and
|
|
82
|
+
shares it across all callers. Only do this when the response genuinely doesn't
|
|
83
|
+
depend on who is calling — otherwise one caller's data leaks to others.
|
|
84
|
+
|
|
85
|
+
:::
|
|
86
|
+
|
|
87
|
+
See the [GraphQL Cache policy reference](../policies/graphql-cache-inbound.mdx)
|
|
88
|
+
for the full option matrix, including how credentialed requests fail safe.
|
|
89
|
+
|
|
90
|
+
## Next steps
|
|
91
|
+
|
|
92
|
+
- [Secure your GraphQL API](./graphql-security.mdx) — complexity limits and
|
|
93
|
+
introspection controls
|
|
94
|
+
- [GraphQL on Zuplo](./graphql.mdx) — set up the endpoint and Dev Portal docs
|
|
95
|
+
- [GraphQL analytics](../analytics/tabs/graphql.md) — watch hit rates and
|
|
96
|
+
latency once traffic flows
|
|
@@ -2,179 +2,190 @@
|
|
|
2
2
|
title: Secure your GraphQL API with Zuplo
|
|
3
3
|
sidebar_label: Secure your GraphQL API
|
|
4
4
|
description:
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
Protect your GraphQL API from denial-of-service attacks and schema discovery
|
|
6
|
+
using Zuplo's GraphQL security policies.
|
|
7
7
|
tags:
|
|
8
8
|
- authentication
|
|
9
9
|
- backends
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
GraphQL gives clients enormous flexibility over what they ask for — which is
|
|
13
|
+
also what makes it easy to abuse. A single request can nest arbitrarily deep,
|
|
14
|
+
fan out into thousands of resolver calls, or map your entire schema through
|
|
15
|
+
introspection. Zuplo ships three policies that close these gaps at the edge,
|
|
16
|
+
before a malicious request ever reaches your origin:
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
[GraphQL Disable Introspection](/docs/policies/graphql-disable-introspection-inbound).
|
|
18
|
+
| Risk | Policy |
|
|
19
|
+
| ------------------- | ------------------------------------------------------------------------------------------------ |
|
|
20
|
+
| Deep / costly query | [`graphql-complexity-limit-inbound`](../policies/graphql-complexity-limit-inbound.mdx) |
|
|
21
|
+
| Schema discovery | [`graphql-disable-introspection-inbound`](../policies/graphql-disable-introspection-inbound.mdx) |
|
|
22
|
+
| Schema over-sharing | [`graphql-introspection-filter-outbound`](../policies/graphql-introspection-filter-outbound.mdx) |
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
This guide explains each risk and shows the policy configuration that addresses
|
|
25
|
+
it. It assumes you've already
|
|
26
|
+
[added a GraphQL endpoint to your gateway](./graphql.mdx).
|
|
22
27
|
|
|
23
|
-
|
|
28
|
+
## Understand the risks
|
|
24
29
|
|
|
25
|
-
|
|
26
|
-
potentially overwhelm your server, resulting in a Denial of Service (DoS)
|
|
27
|
-
attack. This is because deeply nested queries can cause the server to process a
|
|
28
|
-
huge amount of data and operations. While this might be a planned attack, it
|
|
29
|
-
could also be a mistake by a client who is unaware of the query depth limit.
|
|
30
|
+
### Deeply nested or expensive queries
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
Without limits, a client can send a deeply nested query that forces your server
|
|
33
|
+
to resolve a huge graph of data, or a flat-but-expensive query that triggers
|
|
34
|
+
thousands of resolver calls. Either can exhaust resources and cause a
|
|
35
|
+
denial-of-service — whether the client is a deliberate attacker or simply
|
|
36
|
+
unaware of the cost of their query.
|
|
32
37
|
|
|
33
|
-
|
|
34
|
-
force your server to execute resource-intensive operations, potentially slowing
|
|
35
|
-
down the system or causing a DoS.
|
|
38
|
+
### Introspection
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
GraphQL lets clients introspect your schema with `__schema` and `__type`
|
|
41
|
+
queries. This is invaluable during development, but in production it hands
|
|
42
|
+
potential attackers a complete map of your types, fields, and relationships.
|
|
38
43
|
|
|
39
|
-
|
|
40
|
-
during development, it can expose detailed schema information to potential
|
|
41
|
-
attackers in production.
|
|
44
|
+
## Limit query depth and complexity
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
The
|
|
47
|
+
[`graphql-complexity-limit-inbound`](../policies/graphql-complexity-limit-inbound.mdx)
|
|
48
|
+
policy guards against expensive queries in two complementary ways. You can use
|
|
49
|
+
either or both.
|
|
44
50
|
|
|
45
|
-
###
|
|
51
|
+
### Depth limit
|
|
46
52
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
urlRewriteHandler to rewrite the request to your GraphQL API. For this example
|
|
50
|
-
let's assume your GraphQL API is hosted at `https://api.example.com/graphql`.
|
|
53
|
+
A depth limit caps how many levels a query can nest. It needs no schema and is
|
|
54
|
+
the simplest first line of defense. Add it under `useDepthLimit`:
|
|
51
55
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
```json
|
|
56
|
+
```json title="config/policies.json"
|
|
55
57
|
{
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"
|
|
64
|
-
"options": {
|
|
65
|
-
"rewritePattern": "https://api.example.com/graphql"
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
"policies": {
|
|
69
|
-
"inbound": [
|
|
70
|
-
"graphql-complexity-limit-policy",
|
|
71
|
-
"graphql-disable-introspection-policy"
|
|
72
|
-
]
|
|
58
|
+
"name": "graphql-complexity-limit-policy",
|
|
59
|
+
"policyType": "graphql-complexity-limit-inbound",
|
|
60
|
+
"handler": {
|
|
61
|
+
"export": "GraphQLComplexityLimitInboundPolicy",
|
|
62
|
+
"module": "$import(@zuplo/graphql)",
|
|
63
|
+
"options": {
|
|
64
|
+
"useDepthLimit": {
|
|
65
|
+
"depthLimit": 20
|
|
73
66
|
}
|
|
74
|
-
}
|
|
75
|
-
"operationId": "52bdf225-eaa7-441c-afb9-b7df046a142e"
|
|
67
|
+
}
|
|
76
68
|
}
|
|
77
69
|
}
|
|
78
70
|
```
|
|
79
71
|
|
|
80
|
-
|
|
81
|
-
to protect your GraphQL API. These policies can be configured for Zuplo. We'll
|
|
82
|
-
create the configuration for the three policies `graphql-depth-limit`,
|
|
83
|
-
`graphql-complexity-points` and `graphql-disable-introspection-policy` next.
|
|
72
|
+
### Complexity limit
|
|
84
73
|
|
|
85
|
-
|
|
74
|
+
A complexity limit scores each query against your schema and rejects queries
|
|
75
|
+
above a threshold, catching expensive queries that aren't necessarily deep.
|
|
76
|
+
Because it scores against the schema, it needs your GraphQL endpoint URL for
|
|
77
|
+
introspection. Combine it with the depth limit:
|
|
86
78
|
|
|
87
|
-
|
|
88
|
-
[detailed policy documentation](/docs/policies/graphql-complexity-limit-inbound)
|
|
89
|
-
for more information.
|
|
90
|
-
|
|
91
|
-
Add this into your `config/policies.json`
|
|
92
|
-
|
|
93
|
-
```json
|
|
79
|
+
```json title="config/policies.json"
|
|
94
80
|
{
|
|
95
|
-
"
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
"
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
81
|
+
"name": "graphql-complexity-limit-policy",
|
|
82
|
+
"policyType": "graphql-complexity-limit-inbound",
|
|
83
|
+
"handler": {
|
|
84
|
+
"export": "GraphQLComplexityLimitInboundPolicy",
|
|
85
|
+
"module": "$import(@zuplo/graphql)",
|
|
86
|
+
"options": {
|
|
87
|
+
"useDepthLimit": {
|
|
88
|
+
"depthLimit": 20
|
|
89
|
+
},
|
|
90
|
+
"useComplexityLimit": {
|
|
91
|
+
"complexityLimit": 50,
|
|
92
|
+
"endpointUrl": "https://api.example.com/graphql"
|
|
107
93
|
}
|
|
108
94
|
}
|
|
109
|
-
|
|
95
|
+
}
|
|
110
96
|
}
|
|
111
97
|
```
|
|
112
98
|
|
|
113
|
-
|
|
114
|
-
|
|
99
|
+
A query that exceeds either limit is rejected with a `400` and a GraphQL error;
|
|
100
|
+
requests within the limits pass through. See the
|
|
101
|
+
[policy reference](../policies/graphql-complexity-limit-inbound.mdx) for the
|
|
102
|
+
full option set.
|
|
115
103
|
|
|
116
|
-
|
|
104
|
+
## Disable introspection in production
|
|
117
105
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
your
|
|
106
|
+
The
|
|
107
|
+
[`graphql-disable-introspection-inbound`](../policies/graphql-disable-introspection-inbound.mdx)
|
|
108
|
+
policy blocks any query containing `__schema` or `__type` with a `403`, hiding
|
|
109
|
+
your schema from clients. It takes no options:
|
|
122
110
|
|
|
123
|
-
```json
|
|
111
|
+
```json title="config/policies.json"
|
|
124
112
|
{
|
|
125
|
-
"
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
113
|
+
"name": "graphql-disable-introspection-policy",
|
|
114
|
+
"policyType": "graphql-disable-introspection-inbound",
|
|
115
|
+
"handler": {
|
|
116
|
+
"export": "GraphQLDisableIntrospectionInboundPolicy",
|
|
117
|
+
"module": "$import(@zuplo/graphql)",
|
|
118
|
+
"options": {}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Expose a partial schema instead
|
|
124
|
+
|
|
125
|
+
Disabling introspection entirely isn't always what you want. If you need clients
|
|
126
|
+
(or [MCP-connected AI agents](../mcp-server/graphql.mdx)) to introspect part of
|
|
127
|
+
your schema while keeping sensitive types and fields hidden, use the outbound
|
|
128
|
+
[`graphql-introspection-filter-outbound`](../policies/graphql-introspection-filter-outbound.mdx)
|
|
129
|
+
policy. It strips chosen types and fields from introspection responses:
|
|
130
|
+
|
|
131
|
+
```json title="config/policies.json"
|
|
132
|
+
{
|
|
133
|
+
"name": "graphql-introspection-filter-policy",
|
|
134
|
+
"policyType": "graphql-introspection-filter-outbound",
|
|
135
|
+
"handler": {
|
|
136
|
+
"export": "GraphQLIntrospectionFilterOutboundPolicy",
|
|
137
|
+
"module": "$import(@zuplo/graphql)",
|
|
138
|
+
"options": {
|
|
139
|
+
"excludeTypes": ["AdminUser"],
|
|
140
|
+
"excludeTypeFields": {
|
|
141
|
+
"User": ["password", "ssn"],
|
|
142
|
+
"Query": ["adminUsers"]
|
|
141
143
|
}
|
|
142
144
|
}
|
|
143
|
-
|
|
145
|
+
}
|
|
144
146
|
}
|
|
145
147
|
```
|
|
146
148
|
|
|
147
|
-
|
|
148
|
-
by sending overly complicated queries.
|
|
149
|
-
|
|
150
|
-
### c. GraphQL Disable Introspection
|
|
149
|
+
## Attach the policies to your route
|
|
151
150
|
|
|
152
|
-
|
|
151
|
+
Add the inbound policies to your GraphQL route in `routes.oas.json`. The
|
|
152
|
+
outbound filter policy goes in the `outbound` array:
|
|
153
153
|
|
|
154
|
-
```json
|
|
154
|
+
```json title="config/routes.oas.json"
|
|
155
155
|
{
|
|
156
|
-
"
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
156
|
+
"post": {
|
|
157
|
+
"summary": "GraphQL Endpoint",
|
|
158
|
+
"x-graphql": true,
|
|
159
|
+
"x-zuplo-route": {
|
|
160
|
+
"corsPolicy": "none",
|
|
160
161
|
"handler": {
|
|
161
|
-
"export": "
|
|
162
|
-
"module": "$import(@zuplo/
|
|
163
|
-
"options": {
|
|
162
|
+
"export": "urlRewriteHandler",
|
|
163
|
+
"module": "$import(@zuplo/runtime)",
|
|
164
|
+
"options": {
|
|
165
|
+
"rewritePattern": "https://api.example.com/graphql"
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
"policies": {
|
|
169
|
+
"inbound": [
|
|
170
|
+
"graphql-complexity-limit-policy",
|
|
171
|
+
"graphql-disable-introspection-policy"
|
|
172
|
+
]
|
|
164
173
|
}
|
|
165
|
-
}
|
|
166
|
-
|
|
174
|
+
},
|
|
175
|
+
"operationId": "graphql-secure"
|
|
176
|
+
}
|
|
167
177
|
}
|
|
168
178
|
```
|
|
169
179
|
|
|
170
|
-
|
|
171
|
-
|
|
180
|
+
## Example repository
|
|
181
|
+
|
|
182
|
+
For a complete, runnable setup, see the
|
|
183
|
+
[GraphQL API with Zuplo example repository](https://github.com/zuplo/zuplo-graphql-example).
|
|
172
184
|
|
|
173
|
-
##
|
|
185
|
+
## Next steps
|
|
174
186
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
to dive deeper.
|
|
187
|
+
- [Cache GraphQL responses](./graphql-caching.mdx) — cut latency with edge
|
|
188
|
+
caching
|
|
189
|
+
- [Test GraphQL queries](./testing-graphql.mdx) — verify your secured endpoint
|
|
190
|
+
- [GraphQL analytics](../analytics/tabs/graphql.md) — surface errors and monitor
|
|
191
|
+
traffic
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: GraphQL support on Zuplo
|
|
3
|
+
sidebar_label: Overview
|
|
4
|
+
description:
|
|
5
|
+
Everything Zuplo supports for GraphQL APIs — proxying, security, caching,
|
|
6
|
+
analytics, Dev Portal docs, and MCP — with links to each guide and policy.
|
|
7
|
+
tags:
|
|
8
|
+
- backends
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Zuplo treats GraphQL as a first-class backend. Proxy your existing GraphQL
|
|
12
|
+
server through the gateway, then layer on security, caching, analytics, and
|
|
13
|
+
documentation using policies built for the GraphQL request shape — where every
|
|
14
|
+
operation is a `POST` to a single URL, so URL-based gateway features don't
|
|
15
|
+
apply.
|
|
16
|
+
|
|
17
|
+
This page summarizes what Zuplo supports for GraphQL and links to each guide and
|
|
18
|
+
policy reference.
|
|
19
|
+
|
|
20
|
+
:::tip{title="New here?"}
|
|
21
|
+
|
|
22
|
+
Start with [Set up an endpoint](./graphql.mdx) to proxy your GraphQL API through
|
|
23
|
+
the gateway, then come back to layer on the capabilities below.
|
|
24
|
+
|
|
25
|
+
:::
|
|
26
|
+
|
|
27
|
+
## Connect and route
|
|
28
|
+
|
|
29
|
+
Proxy any GraphQL server through a `POST /graphql` route using the
|
|
30
|
+
[URL Rewrite handler](../handlers/url-rewrite.mdx). The Route Designer ships a
|
|
31
|
+
**GraphQL Endpoint** template, and the `x-graphql` route extension marks a route
|
|
32
|
+
as GraphQL so the gateway and Dev Portal treat it accordingly.
|
|
33
|
+
|
|
34
|
+
- [Set up an endpoint](./graphql.mdx) — add or mark a GraphQL route
|
|
35
|
+
|
|
36
|
+
## Secure the endpoint
|
|
37
|
+
|
|
38
|
+
GraphQL's flexibility is also its attack surface: a single request can nest
|
|
39
|
+
arbitrarily deep, fan out into thousands of resolver calls, or map your whole
|
|
40
|
+
schema through introspection. Zuplo closes these gaps at the edge.
|
|
41
|
+
|
|
42
|
+
| Risk | What Zuplo does |
|
|
43
|
+
| ------------------- | ------------------------------------------------ |
|
|
44
|
+
| Deep / costly query | Reject queries over a depth or complexity score |
|
|
45
|
+
| Schema discovery | Block introspection in production |
|
|
46
|
+
| Schema over-sharing | Strip chosen types and fields from introspection |
|
|
47
|
+
|
|
48
|
+
- [Secure your GraphQL API](./graphql-security.mdx) — complexity limits and
|
|
49
|
+
introspection controls
|
|
50
|
+
|
|
51
|
+
## Accelerate with caching
|
|
52
|
+
|
|
53
|
+
A normal CDN keys on the URL, so it can't cache GraphQL queries sent as request
|
|
54
|
+
bodies. The GraphQL cache parses each query, normalizes it into a canonical
|
|
55
|
+
form, and serves semantically identical queries from one edge cache entry.
|
|
56
|
+
|
|
57
|
+
- [Cache GraphQL responses](./graphql-caching.mdx) — serve repeated queries from
|
|
58
|
+
the edge
|
|
59
|
+
|
|
60
|
+
## Observe traffic
|
|
61
|
+
|
|
62
|
+
Report GraphQL operations and their errors to analytics, even when the upstream
|
|
63
|
+
returns errors inside a `200` response. The GraphQL analytics dashboard surfaces
|
|
64
|
+
operation volume, latency, and error classes once traffic flows.
|
|
65
|
+
|
|
66
|
+
- [GraphQL analytics](../analytics/tabs/graphql.md) — monitor operations,
|
|
67
|
+
latency, and errors
|
|
68
|
+
|
|
69
|
+
## Document in the Dev Portal
|
|
70
|
+
|
|
71
|
+
The `@zudoku/plugin-graphql` package renders a browsable type reference and an
|
|
72
|
+
interactive playground in your Dev Portal, generated from your schema or a live
|
|
73
|
+
endpoint. Register one instance per API.
|
|
74
|
+
|
|
75
|
+
- [Document the API in your Dev Portal](./graphql.mdx#document-the-api-in-your-dev-portal)
|
|
76
|
+
— add the GraphQL plugin
|
|
77
|
+
|
|
78
|
+
## Expose GraphQL to AI agents
|
|
79
|
+
|
|
80
|
+
Turn GraphQL queries into MCP tools so AI agents can call your API through the
|
|
81
|
+
Model Context Protocol, with the same policies and auth applied.
|
|
82
|
+
|
|
83
|
+
- [MCP Server GraphQL endpoints](../mcp-server/graphql.mdx) — expose queries as
|
|
84
|
+
MCP tools
|
|
85
|
+
|
|
86
|
+
## Policies at a glance
|
|
87
|
+
|
|
88
|
+
Every GraphQL-aware policy Zuplo ships, with its direction and purpose:
|
|
89
|
+
|
|
90
|
+
| Policy | Direction | Purpose |
|
|
91
|
+
| ------------------------------------------------------------------------------------------------ | --------- | ------------------------------------------------- |
|
|
92
|
+
| [`graphql-cache-inbound`](../policies/graphql-cache-inbound.mdx) | Inbound | Cache repeated queries at the edge |
|
|
93
|
+
| [`graphql-complexity-limit-inbound`](../policies/graphql-complexity-limit-inbound.mdx) | Inbound | Reject queries that are too deep or too expensive |
|
|
94
|
+
| [`graphql-disable-introspection-inbound`](../policies/graphql-disable-introspection-inbound.mdx) | Inbound | Block schema introspection in production |
|
|
95
|
+
| [`graphql-introspection-filter-outbound`](../policies/graphql-introspection-filter-outbound.mdx) | Outbound | Hide chosen types and fields from introspection |
|
|
96
|
+
| [`graphql-analytics-outbound`](../policies/graphql-analytics-outbound.mdx) | Outbound | Report operations and errors to analytics |
|
|
97
|
+
|
|
98
|
+
## Next steps
|
|
99
|
+
|
|
100
|
+
- [Set up an endpoint](./graphql.mdx) — proxy your GraphQL API through the
|
|
101
|
+
gateway
|
|
102
|
+
- [Secure your GraphQL API](./graphql-security.mdx) — complexity limits and
|
|
103
|
+
introspection controls
|
|
104
|
+
- [Cache GraphQL responses](./graphql-caching.mdx) — cut latency with edge
|
|
105
|
+
caching
|
|
106
|
+
- [MCP Server GraphQL endpoints](../mcp-server/graphql.mdx) — expose queries to
|
|
107
|
+
AI agents
|
|
@@ -18,6 +18,7 @@ your schema in the Dev Portal. This guide walks you through setting it up.
|
|
|
18
18
|
Rewrite handler
|
|
19
19
|
- [ ] Tag the route with the `x-graphql` extension
|
|
20
20
|
- [ ] Surface failed operations with the `graphql-analytics-outbound` policy
|
|
21
|
+
- [ ] Add caching and security policies to protect and accelerate the endpoint
|
|
21
22
|
- [ ] Add the `graphqlPlugin` to the Developer Portal
|
|
22
23
|
|
|
23
24
|
:::
|
|
@@ -111,6 +112,23 @@ To fine-tune analytics, see the
|
|
|
111
112
|
[GraphQL Analytics policy reference](../policies/graphql-analytics-outbound.mdx)
|
|
112
113
|
for the full set of options.
|
|
113
114
|
|
|
115
|
+
## Protect and accelerate the endpoint
|
|
116
|
+
|
|
117
|
+
Once requests are flowing, attach GraphQL-aware policies to the route. Zuplo
|
|
118
|
+
ships a set built specifically for the GraphQL request shape:
|
|
119
|
+
|
|
120
|
+
| Goal | Policy |
|
|
121
|
+
| ------------------------------------------ | ------------------------------------------------------------------------------------------------ |
|
|
122
|
+
| Cache repeated queries at the edge | [`graphql-cache-inbound`](../policies/graphql-cache-inbound.mdx) |
|
|
123
|
+
| Block deep or expensive queries | [`graphql-complexity-limit-inbound`](../policies/graphql-complexity-limit-inbound.mdx) |
|
|
124
|
+
| Disable schema introspection in production | [`graphql-disable-introspection-inbound`](../policies/graphql-disable-introspection-inbound.mdx) |
|
|
125
|
+
| Hide sensitive types from introspection | [`graphql-introspection-filter-outbound`](../policies/graphql-introspection-filter-outbound.mdx) |
|
|
126
|
+
| Report failed operations to analytics | [`graphql-analytics-outbound`](../policies/graphql-analytics-outbound.mdx) |
|
|
127
|
+
|
|
128
|
+
See [Secure your GraphQL API](./graphql-security.mdx) for the complexity and
|
|
129
|
+
introspection policies, and [Cache GraphQL responses](./graphql-caching.mdx) to
|
|
130
|
+
serve identical queries from the edge.
|
|
131
|
+
|
|
114
132
|
## Document the API in your Dev Portal
|
|
115
133
|
|
|
116
134
|
The `@zudoku/plugin-graphql` package renders a browsable type reference and a
|
|
@@ -161,6 +179,8 @@ the playground is pointed at the right endpoint.
|
|
|
161
179
|
|
|
162
180
|
- [Secure your GraphQL API](./graphql-security.mdx) — complexity limits and
|
|
163
181
|
introspection policies
|
|
182
|
+
- [Cache GraphQL responses](./graphql-caching.mdx) — serve repeated queries from
|
|
183
|
+
the edge
|
|
164
184
|
- [Testing GraphQL queries](./testing-graphql.mdx) — test the endpoint from the
|
|
165
185
|
Zuplo Portal or external tools
|
|
166
186
|
- [GraphQL analytics](../analytics/tabs/graphql.md) — monitor operation volume,
|
|
@@ -46,16 +46,17 @@ For more information, see the
|
|
|
46
46
|
|
|
47
47
|
## 3. Self-Hosted (On-Premises)
|
|
48
48
|
|
|
49
|
-
Run Zuplo on your own infrastructure in any cloud or private data center
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
Run Zuplo on your own infrastructure in any cloud or private data center. Zuplo
|
|
50
|
+
Self-Hosted runs exclusively on Kubernetes and is installed with a single Helm
|
|
51
|
+
chart into your cluster.
|
|
52
|
+
|
|
53
|
+
In the standard hybrid deployment model, you run the Zuplo API Gateway and its
|
|
54
|
+
management plane on your Kubernetes cluster, while a small set of Zuplo cloud
|
|
55
|
+
services provides supporting features such as deployment configuration and API
|
|
56
|
+
key management. All API traffic to your gateways stays on your infrastructure.
|
|
57
|
+
For environments with strict egress restrictions,
|
|
58
|
+
[book a meeting](https://zuplo.com/meeting) to review your requirements with the
|
|
59
|
+
Zuplo team.
|
|
59
60
|
|
|
60
61
|
Self-Hosted is ideal for organizations that need:
|
|
61
62
|
|
|
@@ -63,8 +64,8 @@ Self-Hosted is ideal for organizations that need:
|
|
|
63
64
|
- To run Zuplo in a private data center or on-premises Kubernetes environment
|
|
64
65
|
- To meet strict data sovereignty or regulatory requirements
|
|
65
66
|
- To integrate with existing on-premises systems and networks
|
|
66
|
-
- To customize the deployment architecture to match your specific requirements
|
|
67
67
|
|
|
68
68
|
For more information, see the
|
|
69
|
-
[Self-Hosted documentation](/docs/self-hosted/overview)
|
|
70
|
-
[
|
|
69
|
+
[Self-Hosted documentation](/docs/self-hosted/overview.md), including the
|
|
70
|
+
[requirements](/docs/self-hosted/overview.md#requirements) to prepare for a
|
|
71
|
+
deployment, or [book a meeting](https://zuplo.com/meeting) with the Zuplo team.
|