Skip to content

Conversation

@benjie
Copy link
Member

@benjie benjie commented Jan 15, 2026

(Extracted and reworked from the error behaviors PR (#1163) to stand alone, since it has broader utility and can progress faster alone.)

This PR introduces "service capabilities" - a mechanism for GraphQL services to advertise features and configuration that exist outside the type system - for example support for syntax features (executable descriptions, fragment arguments, document directives, etc), details of transport support (websocket endpoint, SSE endpoint, how to be notified of new schema versions), and other details that can enable automated client configuration.

Motivation

In addition to being a prerequisite of the error behaviors proposal, this is a desired feature for composite schemas and a key component of The GraphQL Golden Path Initiative (specifically enabling client auto-configuration).

The goal is that a user can point tooling at a GraphQL endpoint and much of the configuration can be implied based on the service's advertised capabilities - this massively reduces the amount of "out of band" communication required.

Example use cases:

  • Does the service support fragment arguments, or must the client transpile them?
  • Does it support customizing error propagation behavior?
  • Are subscriptions available over WebSockets? If so, what endpoint and protocol?
  • Does it support incremental delivery (@defer/@stream)? Which version?
  • Does it support automated document persistence? Which protocol?

Why "capabilities" not "metadata"?

Capabilities inform automated tooling/client decisions and actions. Metadata is ancillary and often intended for human consumption. The name "capabilities" is intentional - it signals the purpose and discourages using this system as a dumping ground for arbitrary metadata. Every capability should be actionable by clients or tooling.

Design

Introspection via __service: __Service meta-field (similar to __schema: __Schema):

type __Service {
  capabilities: [__Capability!]!
}

type __Capability {
  identifier: String!
  description: String
  value: String
}

Support can be probed via: { __type(name: "__Service") { name } } - if no result, then the service does not support service capabilities.

SDL syntax

Slightly changed from previous proposals, here's the syntax I now propose:

service {
  "Descriptions on operations and fragments are supported"
  capability graphql.operationDescriptions

  "WebSocket transport is supported via the given endpoint"
  capability example.transport.ws("wss://api.example.com/graphql")
}

extend service {
  capability com.example.customFeature
}

Notes on this choice:

  • Capabilities without "values" should look neat
  • Capabilities with values followed by capabilities without descriptions should not introduce easy syntax clashes (service { capability foo.bar "Value" capability foo.baz } would apply "Value" as the description for foo.baz rather than the value for foo.bar)
  • The extend syntax feels very natural
  • Avoids double-nesting of curly braces
  • Leaves space for additional service properties in future
  • Inserting other keywords between capabilities is acceptable, in the same way that inserting different directives between repeatable directives is acceptable
  • A directive-based syntax could be used (service @capability(identifier: "...", value: "...")), but the service keyword already introduces new syntax so it doesn't seem necessary to be constrained to using ugly directives when a neater and more palatable syntax is available; this also allows for descriptions for each capability.

Capability identifiers

A QualifiedName syntax (Name(.Name)+) inspired by reverse-domain notation encourages global uniqueness. The graphql. prefix is reserved for official GraphQL Foundation specifications (not just the main spec; e.g. graphql.http.* could be used by the GraphQL-over-HTTP spec).

@netlify
Copy link

netlify bot commented Jan 15, 2026

Deploy Preview for graphql-spec-draft ready!

Name Link
🔨 Latest commit aa7302b
🔍 Latest deploy log https://app.netlify.com/projects/graphql-spec-draft/deploys/696966df0b4b18000879d238
😎 Deploy Preview https://deploy-preview-1208--graphql-spec-draft.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.


ServiceCapability : Description? capability QualifiedName ServiceCapabilityValue?

ServiceCapabilityValue : ( StringValue )
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not

ServiceCapabilityArgument : ArgumentName: ConstantValue
ServiceCapabilityValue : ( ServiceCapabilityArgument+ )

Basically: why shouldn't we be able to use enum values or integers or have a service take in an input or etc? Seems like it'd be common to need 3-4 flags for one capability to work.

Copy link
Member Author

@benjie benjie Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we don't yet have the struct type which would be ideal for this.

But also, if you need multiple arguments (why?) either use custom serialization or use multiple capabilities.

service {
  capability graphql.ws
  capability graphql.ws.endpoint("ws://...")

  "To avoid thundering herd, multiply this by `(0.5 + rand())`"
  capability graphql.ws.reconnectDelay("3000")

  "The modern protocol"
  capability graphql.ws.protocol.graphqlTransportWs

  "The updated modern protocol"
  capability graphql.ws.protocol.graphqlTransportWs.v2

  "Configuration for V2 protocol"
  capability graphql.ws.protocol.graphqlTransportWs.v2.encoding("utf-8")

  "The legacy protocol"
  capability graphql.ws.protocol.graphqlWs
}

Arguments should be the rarer case, IMO - if in doubt, use a sub-name (like the protocols above) rather than an argument.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have examples that don't fit this well?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants