1001Ferramentas
⚛️Validators

GraphQL Query Validator

Check if a GraphQL query/mutation/subscription is syntactically valid and show the operation tree.

GraphQL query validation: what really happens before execution

GraphQL is a query language and runtime for APIs designed by Facebook in 2012 and open-sourced in 2015. Unlike REST, where each endpoint hardcodes a payload, a GraphQL server exposes a single endpoint that accepts a typed query. Before the server runs the query, it goes through three deterministic phases — parsing, validation and execution — defined by the spec at spec.graphql.org.

Parsing turns the query string into an Abstract Syntax Tree (AST); validation type-checks the AST against the server schema; execution resolves each field against data sources. A valid query is one that succeeds at every step before resolvers run, which means errors are caught early and never charge the database.

Query syntax in one sample

query GetUser($id: ID!) {
  user(id: $id) {
    id
    name
    posts {
      title
      publishedAt
    }
  }
}

Elements at play: the operation type (query, mutation, subscription), an operation name (GetUser), variables ($id: ID!), fields, sub-selections and (optionally) directives, fragments and aliases.

The validation rules defined by the spec

The GraphQL specification defines roughly two dozen named validation rules. Highlights:

  • ScalarLeafs — scalar fields (String, Int, Boolean) cannot carry sub-selections.
  • FieldsOnCorrectType — every field must exist on its parent type.
  • ArgumentsOfCorrectType + RequiredArguments — arguments must match declared types and non-null arguments must be supplied.
  • UniqueFragmentNames, NoUnusedFragments, NoFragmentCycles — fragment hygiene.
  • FragmentsOnCompositeTypes — fragments are only valid on object, interface and union types.
  • KnownDirectives, UniqueDirectivesPerLocation — directives must exist and not collide.
  • VariablesAreInputTypes, VariablesInAllowedPosition — variables are constrained to input types and proper positions.

Tooling: from graphql.js to Hasura

  • graphql.js — the JavaScript reference implementation; ships the parser, validator and executor and is reused by virtually every Node server.
  • Apollo Server and GraphQL Yoga — full-featured Node servers built on top of graphql.js.
  • Hasura — instant GraphQL over Postgres, MySQL and SQL Server with auto-generated schema.
  • Strawberry (Python), Async-graphql (Rust), JuniperGraphQL (Rust), gqlgen (Go) — strong implementations outside the Node world.
  • graphql-codegen — generates TypeScript types and typed hooks from the schema and operations.
  • graphql-tools/validate — pluggable validator used in custom pipelines.

Schema-first vs code-first design

Two opposing styles dominate:

  • Schema-first — write SDL (type Query { ... }) in .graphql files and bind resolvers later. Documentation lives next to the contract.
  • Code-first — define types using decorators or builders in your language (NestJS, Strawberry, Pothos). The schema is derived from code, keeping TypeScript / Python types as the single source of truth.

Operational concerns: N+1, depth limits, persisted queries

Production GraphQL has a few classic pitfalls:

  • N+1 problem — naive resolvers issue one DB query per child node. Solved with the DataLoader pattern (per-request batch + cache).
  • Depth and complexity limits — public APIs must guard against deeply nested queries used as DoS. Plugins like apollo-server-plugin-depth-limit and graphql-cost-analysis reject queries above a threshold.
  • Introspection — clients can query __schema to discover types; production deployments often disable introspection to slow down reconnaissance.
  • Persisted queries — clients send a hash instead of the full query string; server matches it against an allow-list, eliminating injection and shrinking payloads.
  • Authorization — applied per-field via directives or middleware, never at the endpoint level (the endpoint is shared).

Federation, REST comparison and the wider ecosystem

Apollo Federation 2 and schema stitching let multiple services contribute to one super-graph, each owning a slice of the types — useful for micro-services. Compared to REST, GraphQL solves both over-fetching (clients ask only for fields they need) and under-fetching (one round-trip per screen rather than several). Compared to gRPC, GraphQL is HTTP-friendly and human-readable, while gRPC wins on binary efficiency for service-to-service traffic.

FAQ

Can I validate a query offline, without hitting the server?

Yes. As long as you have the schema (SDL or introspection JSON), graphql.js parses and validates queries locally — useful in CI to fail PRs that break the contract.

Should I enable a depth limit?

For any public API, yes. A common configuration caps depth at 8-10 and uses cost analysis for cross-field complexity. Without it, a single nested query can take down the database.

How is authorization handled?

At the field level, not at the endpoint. Patterns include directive-based (@auth), middleware in resolvers or graph-level wrappers like graphql-shield.

Does this tool require the schema?

No. It runs lightweight syntax-level validation on the query string: detects operation type, name, variables and the field tree. Full type checking requires the schema and is typically done in CI with graphql-codegen or graphql-eslint.

Can I disable introspection in production?

Yes — Apollo Server, Yoga and most frameworks expose a flag (introspection: false). It is a defense-in-depth measure rather than a security boundary; combine with authentication and persisted queries.

Related Tools