Versioning
Unlike REST APIs, GraphQL schemas do not use URL-based versioning (like /graphql/v2). Most schema changes are additive and non-breaking: adding new types and new fields does not affect existing queries. Removing a field or changing its nullability, however, is a breaking change.
GraphQL provides two directives to manage the lifecycle of schema elements:
@deprecatedsignals that a field is being phased out and consumers should migrate away.@requiresOptInsignals that a field is not yet stable and requires explicit consumer consent.
type Query { users: [User] @deprecated(reason: "Use the `authors` field instead") authors: [User] recommendations: [Book] @requiresOptIn(feature: "experimentalRecommendations")}
Deprecation
You can deprecate output fields, input fields, arguments, and enum values. Deprecated elements remain functional but are flagged in introspection, warning consumers to migrate.
// Types/BookQueries.cs[QueryType]public static partial class BookQueries{ [GraphQLDeprecated("Use the `authors` field instead")] public static User[] GetUsers() { // ... }
public static User[] GetAuthors() { // ... }}
The .NET [Obsolete("reason")] attribute works the same way as [GraphQLDeprecated("reason")].
Warning: You cannot deprecate non-null arguments or input fields that have no default value. Deprecating a required field would silently break queries that depend on it.
Opt-In Features
While @deprecated marks fields that are going away, @requiresOptIn marks fields that are not yet stable. This is useful for rolling out experimental features, expensive operations, or anything where consumers should make a deliberate choice to use it.
Fields marked with @requiresOptIn are hidden from introspection by default. Consumers opt in by specifying the feature name.
Enabling Opt-In Features
Opt-in feature support is disabled by default. Enable it in your schema options:
// Program.csbuilder .AddGraphQL() .ModifyOptions(o => o.EnableOptInFeatures = true);
Marking Fields as Opt-In
Apply @requiresOptIn to output fields, input fields, arguments, and enum values. The directive is repeatable, so a single field can require multiple features.
// Types/Session.cspublic class Session{ public string Id { get; set; } public string Title { get; set; }
[RequiresOptIn("experimentalInstantApi")] public Instant? StartInstant { get; set; }
[RequiresOptIn("experimentalInstantApi")] public Instant? EndInstant { get; set; }}
Warning: Like
@deprecated, you cannot apply@requiresOptInto non-null arguments or input fields without a default value. Hiding a required field would break queries.
Introspection
Consumers discover opt-in fields by passing the includeOptIn argument:
{ __type(name: "Session") { fields(includeOptIn: ["experimentalInstantApi"]) { name requiresOptIn } }}
The includeOptIn argument is available on fields, args, inputFields, and enumValues in introspection queries.
To discover all opt-in features in the schema:
{ __schema { optInFeatures }}
Feature Stability
You can declare the stability level of each opt-in feature. This helps consumers understand whether a feature is experimental, preview, or has some other status.
// Program.csbuilder .AddGraphQL() .ModifyOptions(o => o.EnableOptInFeatures = true) .OptInFeatureStability("experimentalInstantApi", "experimental");
Consumers query feature stability through introspection:
{ __schema { optInFeatureStability { feature stability } }}
Next Steps
- Need to add descriptions? See Documentation.
- Need to create custom directives? See Directives.
- Need to understand schema evolution? See Extending Types.