The problem
gqlgen has extension.ComplexityLimit and there's a third-party depth-limit extension too, but
both only reject — they abort the request once a threshold is crossed. That's useful, but it
gives you zero visibility into what your traffic actually looks like before you pick those
limits.
Three numbers, no rejecting
gqlgen-query-analyzer is a small gqlgen extension that looks at the shape of each incoming
query and reports three numbers, without ever blocking the request:
- Depth — how deeply nested the query is
- FieldCount — total number of fields selected
- ResolverCount — only the fields that actually trigger a separate backend call
The last one is the interesting part. Take a query like orders { id total salesman { fullname } }.
That's 5 fields total, nested 3 levels deep — but only 2 real resolver calls: orders and
salesman. fullname is just reading a property off the salesman object that's already been
fetched, not a new DB/gRPC/DataLoader call. FieldCount treats all 5 the same; ResolverCount tells
you it's really only 2 backend calls.
It's purely static — just looking at the query text, no runtime data — so it doesn't know about
pagination (first/last args) or how many items a list actually returns.
It reports, it doesn't block
Everything goes out via Prometheus metrics, OpenTelemetry span attributes, and optionally the
response's extensions field. Even past every configured threshold, the request still runs and
returns real data — the goal is to see your traffic shape first, then decide where to draw hard
limits with something like ComplexityLimit.
Wiring it in
srv := handler.NewDefaultServer(es)
srv.Use(queryanalysis.New(queryanalysis.Config{
MaxDepth: 10,
MaxFieldCount: 200,
MaxResolverCount: 50,
ExposeInResponse: true,
}))
One Server.Use(...) call, nothing else changes.
Links
- Repo: https://github.com/duc2407/gqlgen-query-analyzer
- Docs: https://pkg.go.dev/github.com/duc2407/gqlgen-query-analyzer
- MIT licensed, Go 1.23+, 95% test coverage
Just shipped v1.0.0 — feedback welcome.
Top comments (0)