Query
query OperationName($var: Type) { field(arg: $var) { subField } }
A GraphQL query is a read-only fetch operation. The server must not modify any data when executing a query. Queries are side-effect free and may be cached. Multiple queries can be sent in a single document; the optional operationName field selects which one to execute. Queries may be anonymous (omit the query keyword) but named queries are strongly recommended for debugging and APM tooling.
How it works
Queries are the primary way to read data from a GraphQL API. They mirror GET semantics in REST – they fetch data without modifying state.
A query consists of a set of fields to select, optionally with arguments to filter or parameterize. Fields may be nested to any depth, letting you fetch related data in one round trip without multiple REST endpoints.
Variables: instead of interpolating values into the query string, declare variables with $ prefix and pass them in the variables JSON object of the request. This separates the query structure from its parameters, enables query plan reuse, and prevents injection attacks.
Fragments: reusable sets of fields defined with the fragment keyword and spread with ... notation. Inline fragments (... on TypeName) enable polymorphic selection on interface and union types.
Aliases: rename a field in the response. Required when selecting the same field twice with different arguments: { alice: user(id: 1) { name } bob: user(id: 2) { name } }.
Directives: @include(if: Boolean) and @skip(if: Boolean) conditionally include or exclude fields based on variable values. Defined in the spec §3.13.
Anonymous queries: omitting the query keyword is valid for a single anonymous operation. Best practice is to always name queries so they appear in logs and APM dashboards.
Examples
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}query GetUserPosts($userId: ID!) {
user(id: $userId) {
name
posts(first: 10, orderBy: CREATED_AT_DESC) {
title
publishedAt
author {
name
}
}
}
}fragment UserFields on User {
id
name
email
}
query GetUsers {
me { ...UserFields }
admin: user(id: "1") { ...UserFields }
}query Search($term: String!) {
search(query: $term) {
... on User { name email }
... on Post { title body }
... on Tag { label }
}
}query ComparePrices {
usd: product(id: "1") { price(currency: USD) }
eur: product(id: "1") { price(currency: EUR) }
}query GetProfile($withEmail: Boolean!) {
user(id: "1") {
name
email @include(if: $withEmail)
}
}Spec Rules
Queries MUST NOT produce side effects – any mutation masquerading as a query is a schema design error
Anonymous queries (no query keyword) are only valid when the document contains exactly one operation
Variables must be declared in the operation signature before use: query Op($id: ID!) { ... }
Non-null variables ($id: ID!) must always be provided; nullable variables ($id: ID) may be omitted
Circular fragment spreads are forbidden – the spec validation rejects infinite recursion
Introspection queries (__schema, __type) are queries and follow all the same rules