CONCEPT Cited by 1 source
Draft schema field¶
Definition¶
A draft schema field is a GraphQL field marked — typically by a custom directive — as "not ready for production". The persist step that registers queries into the persisted-queries DB refuses to register any query that references a draft field. The field can be present in the deployed schema, can be queried in development, and can evolve breaking changes freely — but no production-bound UI bundle can embed an ID that touches it.
At Zalando, the directive is spelled
@draft on FIELD_DEFINITION
(Source: sources/2022-02-16-zalando-graphql-persisted-queries-and-schema-stability).
Mechanism (Zalando)¶
Directive declaration:
Applied to a field:
Persist-time validation rule (JavaScript, verbatim from the post):
export function draftRule(context) {
return {
Field(node) {
const parentType = context.getParentType();
const field = parentType.getFields()[node.name.value];
const isDraft = field.astNode.directives.some(
(directive) => directive.name.value === "draft"
);
if (isDraft) {
context.reportError(new GraphQLError(`Cannot persist draft field`));
}
},
};
}
The rule is supplied to standard
GraphQL validation,
which walks the query AST and emits
"Cannot persist draft field" on any reference
(Source: sources/2022-02-16-zalando-graphql-persisted-queries-and-schema-stability).
The three guarantees¶
Draft gives a schema change three properties simultaneously that are hard to get otherwise:
- Cannot reach production. Persist-time validation refuses to accept queries that use the field.
- Breakable at will. Because only persisted queries run in production, and no persisted query references the draft field, its type, name, and semantics can be changed without deprecation ceremony.
- Lives on mainline. The schema change can be merged to the main deployment branch and even deployed.
The third point is what distinguishes directive-based lifecycle from branch deployments: the unreleased change is already in production infrastructure, so cross-service coordination (resolvers, backing-service contracts) can be built out on mainline instead of a fork (Source: sources/2022-02-16-zalando-graphql-persisted-queries-and-schema-stability).
Why branch deployments are the rejected alternative¶
The Zalando post explicitly rejects branch deployments at their topology: "we have microservices and the GraphQL layer is an aggregator from multiple other services. So, maintaining multiple feature branches across 3-5 projects for 1 or 2 product features isn't going to help any developer or team move smoothly. The complexity increases non-linearly as we mix different features that must work together." Draft moves the gate from "which branch is deployed" to "which queries persist," which composes across services better (Source: sources/2022-02-16-zalando-graphql-persisted-queries-and-schema-stability).
Draft vs. deprecation¶
@draft— field is new, not-yet-used, breakable. Usage direction: pre-production. Persist-blocked.@deprecated(standard GraphQL) — field was used, is being removed. Usage direction: post-production. Queries still persist; tooling surfaces a warning.
Both are directive-based schema annotations, but they operate at opposite ends of the field lifecycle.
Lifecycle placement¶
Draft is the first stage in Zalando's three-stage directive-based field lifecycle (see patterns/directive-based-field-lifecycle):
@draft → @allowedFor(components: [...]) → (no directive)
(unpersistable) (component-scoped, persistable (stable, unrestricted)
within whitelist)
When the field stabilises, both @draft and (if applied)
@allowedFor annotations are removed, and the field joins
the "non-breaking contract" underwritten by persisted
queries.
Seen in¶
- Zalando UBFF — the canonical implementation (sources/2022-02-16-zalando-graphql-persisted-queries-and-schema-stability, systems/zalando-graphql-ubff).
Related¶
- concepts/graphql-persisted-queries — the mechanism
that gives
@draftits teeth (no persist, no prod). - concepts/component-scoped-field-access — the next lifecycle stage.
- concepts/schema-evolution — the larger problem draft addresses.
- concepts/feature-flag — draft is schema-level, not runtime-level; feature flags gate behaviour at request time, draft gates at build time.
- patterns/directive-based-field-lifecycle — the umbrella pattern.
- patterns/automatic-persisted-queries — the mechanism class draft rides on.
- systems/graphql — the substrate.
- systems/zalando-graphql-ubff — where draft lives in production.