DEV Community

Nguyễn Minh Đức
Nguyễn Minh Đức

Posted on

FieldCount vs ResolverCount: a small but important distinction for GraphQL query cost in gqlgen

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,
}))
Enter fullscreen mode Exit fullscreen mode

One Server.Use(...) call, nothing else changes.

Links

Just shipped v1.0.0 — feedback welcome.

Top comments (0)