Skip to content

CONCEPT Cited by 2 sources

GraphQL schema directive

Definition

A GraphQL schema directive is a directive whose declaration permits it on one or more TypeSystem locations — the parts of the schema that describe types rather than executable queries. Schema directives declare server-side behaviour, validation, or resolution logic attached to a schema element; clients never write schema directives in their queries.

The GraphQL spec enumerates 11 TypeSystem locations a directive may target:

Location Example
SCHEMA schema @foo { query: Query }
SCALAR scalar x @foo
OBJECT type Product @foo { }
FIELD_DEFINITION type X { field: String @foo }
ARGUMENT_DEFINITION type X { field(arg: Int @foo): String }
INTERFACE interface X @foo {}
UNION union X @foo = A \| B
ENUM enum X @foo { A B }
ENUM_VALUE enum X { A @foo B }
INPUT_OBJECT input X @foo { }
INPUT_FIELD_DEFINITION input X { field: String @foo }

The spec's built-in schema directives are @deprecated and @specifiedBy; everything else is custom per-organisation.

Contrast with query directives

The partner primitive query directive targets ExecutableDefinition locations — the parts of queries, not schemas (QUERY, MUTATION, FIELD, FRAGMENT_DEFINITION, etc.). The two directive classes carry different purposes:

Aspect Schema directive Query directive
Written by Schema author (server team) Client author
Carries Server-side behaviour / validation / resolver logic Client-side metadata
Lifecycle Tied to the deployed schema Embedded in the operation
Example @isAuthenticated (authorize) @tracingTag (tag the span)
Composable with resolvers Yes (graphql-tools wraps) No (extract from AST instead)

Zalando's 2023 survey names the distinction directly: "the query directives are generally useful for clients to express certain types of metadata for the query. The schema directives are generally useful for declaratively specifying common server-side behaviors, for example, authorization requirements, marking sensitive data, etc." (Source: sources/2023-10-18-zalando-understanding-graphql-directives-practical-use-cases-at-zalando).

Implementation idiom

Schema directives are typically implemented by wrapping the resolver: the-guild's graphql-tools library exposes a mapSchema transform that visits each annotated field and composes the directive's logic before, after, or around the existing resolver:

// Illustration of schema directives execution in
// the query execution pipeline
const resolvers = {
  Query: {
    async product(_, { id }) {
      // schema directives (pre)
      schemaDirectivesExecutions();

      // resolver logic
      const product = await getProduct(id);

      // schema directives (post)
      schemaDirectivesExecutions();

      return product;
    },
  },
};

The directive function takes the original resolver and returns a new resolver, letting the schema express authorization, logging, caching, or validation declaratively without polluting each resolver body with the same wrapper.

Why schema directives win at scale

  • Declarative. The schema itself documents which fields require auth, which arguments are PII, which enums are stable. Readers see the policy without reading resolver code.
  • Server-controlled. The schema author can change the directive's semantics without client coordination — clients never see or write them.
  • Breaking-change-friendly. Renaming a directive or changing its arguments does not break any persisted query (unlike changing a field name or argument type), because clients do not reference schema directives by name.
  • Composable with other server-side tooling. Schema linters can inspect directive presence and fail builds; build-time codegen can emit boilerplate based on directive annotations (patterns/directive-driven-entity-codegen).

Seen in

Last updated · 501 distilled / 1,218 read