Writing plugins
A plugin is a Rust value implementing Plugin<Ctx>. It can inspect field metadata, mutate the
field definition, wrap resolvers or subscriptions, and configure the schema builder.
Plugin requires Send + Sync + 'static because Schema is Clone and a built schema may be
used from any thread. Put shared mutable state behind Arc<Mutex<...>> or atomics.
Field metadata
FieldMeta describes one field after interface inheritance and type overrides:
meta.path() returns "Type.field".
The field hook can change the description, deprecation reason, complexity, directives,
arguments or type.
Wrapping a resolver
wrap_resolve receives the next resolver and returns a new one. Call next(context).await to
preserve the original behaviour, or return directly to replace it.
A wrapping plugin sees the same ResolverContext as the resolver, including arguments and
ResolveInfo. Keep the future Send and avoid holding a lock across an await.
Wrapping a field materializes its resolver. A field declared with expose! starts on a
synchronous, allocation-free property path; the first plugin whose wrap_resolve returns a
different function converts it to the boxed path, because the wrapper needs a future to call. A
plugin that only changes metadata (or whose wrap_resolve returns next unchanged) leaves the
synchronous path in place.
wrap_subscribe has the same shape for subscription setup resolvers. It wraps the function that
creates the stream, not each event.
Configuring the schema
configure runs once after every type and root is registered:
Coverage and idempotence
Plugins are applied to the builder's declarations at build time. A type reopened in another
module is visited once with its final fields. build_clone visits the clone, not the original,
so wrapper functions do not stack.
If a plugin only needs field metadata, leave the resolver untouched. That preserves the executor's leaf and raw-object fast paths.
A counting plugin
In production, prefer structured tracing over storing paths in memory. See Tracing.
