mirror of
https://github.com/prdlk/dotfiles.git
synced 2026-11-03 19:37:52 +00:00
gitconfig: name/email from chezmoi data, gh credential helper by PATH lookup (portable to darwin). claude: no credentials, history, projects, sessions, or plugin caches. ssh config as private_ (0600). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
226 lines
6.4 KiB
Markdown
226 lines
6.4 KiB
Markdown
# GraphQL Analytics API Patterns & Best Practices
|
|
|
|
## Time-Series Queries
|
|
|
|
Use time dimension granularity matching your range (see Best Practices below).
|
|
|
|
```graphql
|
|
query TrafficTimeSeries($zoneTag: string!, $start: Time!, $end: Time!) {
|
|
viewer {
|
|
zones(filter: { zoneTag: $zoneTag }) {
|
|
httpRequestsAdaptiveGroups(
|
|
filter: { datetime_gt: $start, datetime_lt: $end }
|
|
limit: 1000
|
|
orderBy: [datetimeFiveMinutes_ASC] # or datetimeHour_ASC for longer ranges
|
|
) {
|
|
count
|
|
dimensions { datetimeFiveMinutes }
|
|
sum { edgeResponseBytes }
|
|
ratio { status4xx status5xx }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Top-N Queries
|
|
|
|
### Top Countries by Request Count
|
|
|
|
```graphql
|
|
query TopCountries($zoneTag: string!, $start: Time!, $end: Time!) {
|
|
viewer {
|
|
zones(filter: { zoneTag: $zoneTag }) {
|
|
httpRequestsAdaptiveGroups(
|
|
filter: { datetime_gt: $start, datetime_lt: $end }
|
|
limit: 10
|
|
orderBy: [count_DESC]
|
|
) {
|
|
count
|
|
dimensions { clientCountryName }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Use `orderBy: [sum_edgeResponseBytes_DESC]` for top paths by bandwidth. Add `edgeResponseStatus_geq: 400` to the filter for top error status codes.
|
|
|
|
## Workers Analytics
|
|
|
|
```graphql
|
|
query WorkersOverview($accountTag: string!, $start: Time!, $end: Time!) {
|
|
viewer {
|
|
accounts(filter: { accountTag: $accountTag }) {
|
|
workersInvocationsAdaptive(
|
|
filter: { datetime_gt: $start, datetime_lt: $end }
|
|
limit: 100
|
|
orderBy: [sum_requests_DESC]
|
|
) {
|
|
sum { requests errors subrequests wallTime }
|
|
quantiles { cpuTimeP50 cpuTimeP99 wallTimeP50 wallTimeP99 }
|
|
dimensions { scriptName }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Filter by `scriptName` for a specific Worker. Add `datetimeFiveMinutes` dimension + `orderBy: [datetimeFiveMinutes_ASC]` for error rate over time.
|
|
|
|
## Firewall / Security
|
|
|
|
```graphql
|
|
query RecentFirewallEvents($zoneTag: string!, $start: Time!) {
|
|
viewer {
|
|
zones(filter: { zoneTag: $zoneTag }) {
|
|
firewallEventsAdaptive(
|
|
filter: { datetime_gt: $start }
|
|
limit: 50
|
|
orderBy: [datetime_DESC]
|
|
) {
|
|
action source clientIP clientCountryName userAgent
|
|
clientRequestHTTPHost clientRequestPath ruleId datetime
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
For aggregated firewall stats, use `firewallEventsAdaptiveGroups` with `action: "block"` filter and group by `ruleId`, `source`, `datetimeHour`.
|
|
|
|
## DNS Analytics
|
|
|
|
```graphql
|
|
query DNSQueryVolume($zoneTag: string!, $start: Time!, $end: Time!) {
|
|
viewer {
|
|
zones(filter: { zoneTag: $zoneTag }) {
|
|
dnsAnalyticsAdaptiveGroups(
|
|
filter: { datetime_gt: $start, datetime_lt: $end }
|
|
limit: 500
|
|
orderBy: [datetimeFiveMinutes_ASC]
|
|
) {
|
|
count
|
|
dimensions { datetimeFiveMinutes }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Storage Analytics (Account-Scoped)
|
|
|
|
R2, KV, and D1 use `date` (Date type) filters instead of `datetime` (Time type).
|
|
|
|
```graphql
|
|
# R2 operations
|
|
r2OperationsAdaptiveGroups(filter: { date_geq: $start, date_leq: $end }, limit: 100, orderBy: [date_DESC]) {
|
|
dimensions { date bucketName actionType }
|
|
sum { requests }
|
|
}
|
|
|
|
# KV operations
|
|
kvOperationsAdaptiveGroups(filter: { date_geq: $start, date_leq: $end }, limit: 100, orderBy: [date_DESC]) {
|
|
dimensions { date actionType }
|
|
sum { requests }
|
|
}
|
|
|
|
# D1 analytics
|
|
d1AnalyticsAdaptiveGroups(filter: { date_geq: $start, date_leq: $end }, limit: 100, orderBy: [date_DESC]) {
|
|
dimensions { date databaseId }
|
|
sum { readQueries writeQueries rowsRead rowsWritten }
|
|
}
|
|
```
|
|
|
|
## Cache Analytics
|
|
|
|
```graphql
|
|
query CacheStatusBreakdown($zoneTag: string!, $start: Time!, $end: Time!) {
|
|
viewer {
|
|
zones(filter: { zoneTag: $zoneTag }) {
|
|
httpRequestsAdaptiveGroups(
|
|
filter: { datetime_gt: $start, datetime_lt: $end }
|
|
limit: 20
|
|
orderBy: [count_DESC]
|
|
) {
|
|
count
|
|
dimensions { cacheStatus }
|
|
sum { edgeResponseBytes }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
For cache hit ratio over time, use aliases to query the same dataset twice — once with `cacheStatus: "hit"` filter and once without — then compute the ratio client-side.
|
|
|
|
## Multi-Dataset Queries
|
|
|
|
A single request can query multiple datasets, avoiding extra HTTP round-trips:
|
|
|
|
```graphql
|
|
query DashboardOverview($zoneTag: string!, $start: Time!, $end: Time!) {
|
|
viewer {
|
|
zones(filter: { zoneTag: $zoneTag }) {
|
|
httpTraffic: httpRequestsAdaptiveGroups(
|
|
filter: { datetime_gt: $start, datetime_lt: $end }, limit: 1
|
|
) { count sum { edgeResponseBytes } ratio { status4xx status5xx } }
|
|
firewallEvents: firewallEventsAdaptiveGroups(
|
|
filter: { datetime_gt: $start, datetime_lt: $end }, limit: 5, orderBy: [count_DESC]
|
|
) { count dimensions { action source } }
|
|
dnsQueries: dnsAnalyticsAdaptiveGroups(
|
|
filter: { datetime_gt: $start, datetime_lt: $end }, limit: 1
|
|
) { count }
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## AI & Gateway Analytics
|
|
|
|
```graphql
|
|
# Workers AI inference
|
|
aiInferenceAdaptiveGroups(
|
|
filter: { datetime_gt: $start, datetime_lt: $end }, limit: 100, orderBy: [datetimeHour_DESC]
|
|
) {
|
|
count
|
|
sum { totalInputTokens totalOutputTokens totalRequestBytesIn }
|
|
dimensions { modelId datetimeHour }
|
|
}
|
|
|
|
# AI Gateway requests
|
|
aiGatewayRequestsAdaptiveGroups(
|
|
filter: { datetime_gt: $start, datetime_lt: $end }, limit: 100, orderBy: [datetimeHour_DESC]
|
|
) {
|
|
count
|
|
dimensions { gateway provider model datetimeHour }
|
|
sum { cachedTokensIn cachedTokensOut uncachedTokensIn uncachedTokensOut }
|
|
}
|
|
```
|
|
|
|
Both are account-scoped — nest under `accounts(filter: { accountTag: $accountTag })`.
|
|
|
|
## Best Practices
|
|
|
|
**Always include time filters.** Queries without time filters scan all data and are slow/expensive.
|
|
|
|
**Match time granularity to range:**
|
|
|
|
| Time Range | Recommended Dimension |
|
|
|------------|----------------------|
|
|
| < 6 hours | `datetimeMinute` or `datetimeFiveMinutes` |
|
|
| 6-48 hours | `datetimeFiveMinutes` or `datetimeFifteenMinutes` |
|
|
| 2-14 days | `datetimeHour` |
|
|
| 14+ days | `date` |
|
|
|
|
**Use aliases** for querying the same dataset with different filters in one request.
|
|
|
|
**Request only needed fields.** Extra dimensions and metrics increase query cost.
|
|
|
|
## See Also
|
|
|
|
- [README.md](README.md) - Overview, decision tree, dataset index
|
|
- [api.md](api.md) - Query structure, aggregation fields, filtering operators
|
|
- [configuration.md](configuration.md) - Authentication, client setup, introspection queries
|
|
- [gotchas.md](gotchas.md) - Rate limits, sampling, troubleshooting
|