grx

Built-in directives

Every directive grx recognises (@skip/@include/@defer/@stream and schema-side @deprecated/@specifiedBy/@oneOf) and how to author them alongside code-first structs.

Use this guide when a query needs conditional or incremental execution, or when schema metadata must appear in introspection. Start with the supported built-ins before designing a custom directive system.

Introspection’s __schema.directives list is fixed to seven built-ins (exec/introspection.go). Executable directives are enforced while flattening selections; schema directives surface through reflection, schema.ScalarConfig, or exec.ParseSDL.

Executable directives beyond this list

Arbitrary directive @example on FIELD declarations that attach custom semantics (like gqlgen or SDL plugins) are not part of schema.Build yet. Use the built-in directives documented on this page or extend resolver behavior in Go.


Supported directives (overview)

NameExecutable / schemaGraphQL introspection locationsArguments surfaced to clients
skipExecutableFIELD, FRAGMENT_SPREAD, INLINE_FRAGMENTif: Boolean!
includeExecutableFIELD, FRAGMENT_SPREAD, INLINE_FRAGMENTif: Boolean!
deferExecutable (incremental delivery)FIELD, FRAGMENT_SPREAD, INLINE_FRAGMENT (repeatable)if: Boolean = true, label: String
streamExecutable (incremental delivery; list-valued fields only)FIELD (repeatable)if: Boolean = true, label: String, initialCount: Int = 0
deprecatedSchemaFIELD_DEFINITION, ARGUMENT_DEFINITION, INPUT_FIELD_DEFINITION, ENUM_VALUEreason: String = No longer supported
specifiedBySchemaSCALARurl: String!
oneOfSchemaINPUT_OBJECT (not repeatable)(none)

Executable directives

@skip / @include

Evaluated automatically when exec merges selection sets (evalSkipInclude). Both require a Boolean if (literals or $variables).

{
  profile {
    id
    nickname @include(if: $withNick)
    oldHandle @skip(if: $production)
  }
}

Client tip

Treat @skip(false) / @include(true) as no-ops; combine both on the same field only when logically consistent - validation rejects impossible directive combinations per Validation.

@defer / @stream (incremental delivery)

@defer delays fragment or spread selections behind the primary response. if: false removes the directive (work runs eagerly). label namespaces incremental patches.

@stream batches list output: the resolver must return Go slices or arrays. initialCount is how many list elements stay in the first data payload; the remainder ship as incremental[] parts. initialCount: 0 skips the eager prefix entirely - every element is scheduled as incremental work (see [completeListStreamed](https://github.com/grx-gql/grx/blob/main/exec/incremental.go)).

Multipart HTTP: set Accept: multipart/mixed so http calls Executor.ExecuteIncremental, wires an incremental collector, and emits @defer / @stream payloads over multipart/mixed (writeIncremental).

Fallback JSON (Execute): Without that Accept header - or inside tests invoking **Execute directly - the executor does not attach an incremental collector. @defer fragments flatten eagerly (collectDefers = false). @stream on lists is ignored, and the resolver’s slice is serialized in one completeValue / list pass exactly like ordinary GraphQL responses.

Regression-style coverage: incremental_delivery_test.go (**multipart round-trips against grx.NewServer).


Schema directives @deprecated

Output fields (code-first structs)

Deprecation metadata uses gql tags, matching FIELD_DEFINITION on struct-backed GraphQL objects:

package graph

type Profile struct {
	ID         string `gql:"id,nonNull"`
	Username   string `gql:"username,nonNull"`
	LegacyName string `gql:"legacyName,deprecated=Use username instead"`
}

Introspection and GraphiQL read isDeprecated + deprecationReason. examples/directives/ bundles a runnable demo.

Arguments, inputs, enums

Built-in @deprecated introspection spans ARGUMENT_DEFINITION, INPUT_FIELD_DEFINITION, and ENUM_VALUE, but today’s schema.Build path wires deprecation only onto struct-backed object fields. Argument/input/enum deprecation requires SDL-driven schema construction (exec.ParseSDL) until builder coverage expands.

Executable examples in sandbox

Combine struct-backed deprecation with client directives:

{
  me {
    id
    username @include(if: true)
    legacyName @skip(if: true)
  }
}

Schema directive @specifiedBy

Use schema.ScalarConfig.SpecifiedByURL when registering custom scalars, or SDL:

scalar DateTime @specifiedBy(url: "https://example.com/scalars/datetime")

Introspection exposes specifiedByURL.


Schema directive @oneOf

Marks input objects so exactly one field may be supplied. grx honours inputType.IsOneOf during coercion (input_coercion.go).

Struct-only authoring gap

Pure schema.Config + structs cannot toggle IsOneOf directly - author SDL (input Foo @oneOf { … }) via exec.ParseSDL or extend the builder.


Debugging & tooling

NeedTip
Enumerate builtins{ __schema { directives { name locations args { name } isRepeatable } } }
Validation errorsMention directive locations sibling merge rules - Execution → Validation
Incremental payloadsInspect multipart/mixed parts or call Executor.ExecuteIncremental from tests

Edit this page on GitHub

Last updated on

On this page