Arguments and Variables

beginner
10 min

Arguments and Variables

Hard-coded queries only get you so far. Real apps need to fetch this user, page that list, and toggle fields based on UI state. GraphQL handles all of it with arguments on fields and variables supplied at execution time. This lesson covers both, plus default values and the built-in @include and @skip directives.

Field Arguments

In REST, parameters live in the URL. In GraphQL, every field can accept named, typed arguments declared in the schema:

graphql
type Query { post(id: ID!): Post posts(limit: Int = 10, offset: Int = 0, published: Boolean): [Post!]! } type User { avatar(size: Int = 64): String! }

Note that arguments are not just for root fields; avatar(size:) lets each client request the image dimensions it actually renders. Arguments can have default values (limit: Int = 10), making them optional.

Why Not String Interpolation?

You might be tempted to build queries like this in JavaScript:

js
// Anti-pattern: do not do this const q = `query { post(id: "${id}") { title } }`;

This defeats query caching, invites injection-style bugs, and breaks static analysis. The correct tool is variables.

Variables: Dynamic Queries Done Right

Variables separate the static query text from the dynamic values. Declare them after the operation name with a $ prefix and a type, then reference them anywhere an argument is expected:

graphql
query GetPost($id: ID!, $withAuthor: Boolean = false) { post(id: $id) { title author @include(if: $withAuthor) { name } } }

The client sends the values in a separate JSON payload:

json
{ "id": "7", "withAuthor": true }

Because the query string never changes, servers and CDNs can cache the parsed document, and tools can validate the operation at build time. The variable type must be compatible with where it is used; an incompatible type, say Int supplied where ID! is expected, is a validation error before execution even starts.

Directives: @include and @skip

The example above used @include(if: $withAuthor). GraphQL ships two execution directives:

  • @include(if: Boolean!) - include this field only when the condition is true.
  • @skip(if: Boolean!) - omit this field when the condition is true.

They let one query serve multiple UI states, for example a compact and an expanded card, without maintaining two documents.

Input Object Types

When a field needs many arguments, bundle them into an input type:

graphql
input PostFilter { published: Boolean authorId: ID search: String } type Query { posts(filter: PostFilter, limit: Int = 10): [Post!]! }

Input types keep signatures tidy and evolve gracefully: adding an optional field to PostFilter breaks no existing clients. They will become essential when we reach mutations in the next lesson.

Key Takeaways

  • Any field, not just root fields, can declare typed arguments with optional defaults.
  • Never interpolate values into query strings; pass them as $variables with a separate JSON payload.
  • Variable declarations are type-checked against argument slots before execution.
  • @include and @skip toggle fields at runtime from a single query document.
  • input types group related arguments and keep APIs evolvable.

Next lesson: Mutations, where we finally start changing data instead of just reading it.

Arguments and Variables - GraphQL | CodeYourCraft | CodeYourCraft