Visibility๐
Visibility is used to hide parts of your schema from selected users. Visibility acts on two layers:
- Introspection queries omit anything the current request can't see.
- Operations that reference a hidden entity fail in the validation phase of the graphql request cycle
with the same error shape that
graphql-coreproduces for genuinely nonexistent types and fields.
Use visibility to control availability, not access, for example to gradually roll out a new field or phase out an old one. Visibility is not a security boundary. Treat it as a way to shape what the schema looks like, not as a way to protect data.
Basic usage๐
Hide a Field from unauthenticated users by decorating a method with the
<field_name>.visible decorator:
Using the following query:
An unauthenticated request that queries the name field sees:
Hide an entire QueryType by overriding the __is_visible__ classmethod:
An unauthenticated request that queries the tasks entrypoint sees:
Notice that hiding the TaskType also hid the tasks entrypoint, even though the
entrypoint itself has no visibility hook. Visibility cascades through type
references. You only need to hide the root of what you want to remove and the rest
follows. The exact cascade rules for each entity are described in the
Supported entities section below.
Supported entities๐
Every Undine class and member exposes the same shape. The paragraph below each snippet describes what happens when that entity is hidden.
RootType๐
null in introspection and
every Entrypoint on that root becomes unreachable.
Entrypoint๐
Hides the entrypoint from its RootType. No further cascade.
QueryType๐
Hides every Entrypoint, Field, InterfaceField, and FederationField that
returns it, every MutationType that has it as its output type, and removes it
from any UnionType's member list. Cascade is transitive, so a MutationType
hidden this way in turn hides any Input or Entrypoint that references it.
FilterSet and OrderSet connected to the QueryType are hidden if not
other entrypoint references them.
Field๐
Hides the field from its QueryType. No further cascade.
MutationType๐
Hides the Entrypoint that references it. Hiding a related MutationType
hides the Input that references it instead.
Input๐
Hides the input from its MutationType. No further cascade.
FilterSet๐
Hides all its Filter members and removes the filter argument from every
entrypoint that uses it, including when the entrypoint is wrapped by pagination
such as Connection or OffsetPagination.
Filter๐
Hides the filter from its FilterSet. No further cascade.
OrderSet๐
Hides all its Order members and removes the orderBy argument from every
entrypoint that uses it, including when the entrypoint is wrapped by pagination
such as Connection or OffsetPagination.
Order๐
Hides the order from its OrderSet. No further cascade.
InterfaceType๐
Hides every Field, Entrypoint, InterfaceField, and FederationField that
returns it. Implementing QueryTypes stay visible and queryable directly, but
they no longer list the hidden interface in their interfaces, and any fields
they gained purely by inheriting the interface disappear as well. Fields defined
directly on the QueryType that happen to share a name with an interface field
stay visible.
InterfaceField๐
Hides the field from its InterfaceType. On every implementing QueryType,
the corresponding inherited Field is also hidden.
UnionType๐
Hides every Field, Entrypoint, InterfaceField, and FederationField that
returns it. Member QueryTypes stay visible on their own. When some (but not
all) members are hidden, they disappear from the union's member list, and a
single-member union is still valid. If every member is hidden at runtime, the
union collapses to empty and is treated as hidden itself, cascading up to every
field or entrypoint that returns it.
Directive๐
Removes the directive from __schema.directives and from every type system location
where it was applied. Queries that apply it in an executable location fail validation.
DirectiveArgument members hide with it.
DirectiveArgument๐
Hides the argument from its Directive. No further cascade.
CalculationArgument๐
Hides the argument from the Field whose ref is a Calculation using this
argument. No further cascade.
FederationType๐
Hides every Entrypoint, Field, InterfaceField, and FederationField
that returns it, and its own FederationField members hide with it.
FederationField๐
Hides the field from its FederationType. No further cascade.
Federation๐
Visibility hooks apply to FederationType and FederationField (see above), and the
_entities resolver honors them just like any other field: entries that the current
request can't see are filtered out of the response.
The _service { sdl } payload is not filtered, however. It always returns the full
subgraph SDL because the router uses it to compose the supergraph, and composition needs
a stable, request-independent view of the schema. Per-request visibility on federation
types therefore doesn't propagate to clients through the supergraph โ the router remains
the source of truth for what clients see.
For hiding a federation element from the supergraph entirely, use the
InaccessibleDirective instead. It marks the
element as @inaccessible, so the router excludes it from the composed schema while it
remains available within the subgraph.
Caching๐
If using response caching, cached is forced as per-user when the traversal encounters any entity that uses visibility. This makes sure hidden data cannot leak between users through cached responses.
Additionally, you can cache the user's introspection response by setting
VISIBILITY_CACHE_TIMEOUT.
Cache keys are derived from the user's primary key plus any extra context supplied by
VISIBILITY_CACHE_EXTRA_CONTEXT.
Caveats๐
- Sync only.
__is_visible__and.visiblecallbacks must be synchronous.graphql-core's introspection resolvers and validation rules are sync, and cannot suspend onawait. If your visibility check needs data that is only reachable through an async fetch, resolve it using a lifecycle hook and store the result on the request object. - Fail-closed on exception. If a hook raises, the entity is treated as hidden
- "did you mean" suggestions. Auto-disabled globally when any schema uses
visibility so hidden entities never leak through error messages. This is equivalent to setting
ALLOW_DID_YOU_MEAN_SUGGESTIONStoFalse.