Help us shape the future of Go! Take the 2026 Go Developer Survey

federation

package
v0.17.97 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 20, 2026 License: MIT Imports: 16 Imported by: 17

README

Federation plugin

Add support for graphql federation in your graphql Go server!

TODO(miguel): add details.

Tests

There are several different tests. Some will process the configuration file directly. You can see those in the federation_test.go. There are also tests for entity resolvers, which will simulate requests from a federation server like Apollo Federation.

Running entity resolver tests.

  1. Go to plugin/federation
  2. Run the command go generate
  3. Run the tests with go test ./....

Architecture

TODO(miguel): add details.

Entity resolvers - GetMany entities

The federation plugin implements GetMany semantics in which entity resolvers get the entire list of representations that need to be resolved. This functionality is currently option tho, and to enable it you need to specify the directive @entityResolver in the federated entity you want this feature for. E.g.

directive @entityResolver(multi: Boolean) on OBJECT

type MultiHello @key(fields: "name") @entityResolver(multi: true) {
    name: String!
}

That allows the federation plugin to generate GetMany resolver function that can take a list of representations to be resolved.

From that entity type, the resolver function would be

func (r *entityResolver) FindManyMultiHellosByName(ctx context.Context, reps []*generated.ManyMultiHellosByNameInput) ([]*generated.MultiHello, error) {
  /// <Your code to resolve the list of items>
}

Note: If you are using omit_slice_element_pointers: true option in your config yaml, your GetMany resolver will still generate in the example above the same signature FindManyMultiHellosByName(ctx context.Context, reps []*generated.ManyMultiHellosByNameInput) ([]*generated.MultiHello, error). But all other instances will continue to honor omit_slice_element_pointers: true

Documentation

Index

Constants

View Source
const DirArgFields = "fields"

The fields arguments must be provided to both key and requires directives.

View Source
const DirArgType = "type"

Tells the code generator what type the directive is referencing

View Source
const DirNameEntityReference = "entityReference"

Variables

This section is empty.

Functions

This section is empty.

Types

type Entity

type Entity struct {
	Name      string // The same name as the type declaration
	Def       *ast.Definition
	Resolvers []*EntityResolver
	Requires  []*Requires
	Multi     bool
	// PerEntity selects one resolver for the entity, taking a representation
	// input, instead of one resolver per @key. Set from the
	// entity_resolver_per_entity option.
	PerEntity bool
	// RepresentationType is the Go type of a per-entity resolver's argument:
	// one representation, or a list of them for a batch resolver.
	RepresentationType *config.TypeReference
	// RequiresStrategy is how this entity's @requires fields are delivered to
	// the resolver. Resolved per entity in buildEntity.
	RequiresStrategy RequiresStrategy
	Type             types.Type
	// ImplDirectives are the resolved non-federation OBJECT-level directives
	// with full type information, populated in GenerateCode for use in the
	// federation template to wrap entity resolver calls.
	ImplDirectives []*codegen.Directive
}

Entity represents a federated type that was declared in the GQL schema.

func (Entity) GetTypeInfo added in v0.17.45

func (e Entity) GetTypeInfo() string

GetTypeInfo - get the imported package & type name combo. package.TypeName

func (*Entity) HasObjectDirectives added in v0.17.87

func (e *Entity) HasObjectDirectives() bool

HasObjectDirectives reports whether this entity's type definition carries any non-federation user-defined directives that should be applied at runtime when the entity is resolved through the _entities query.

func (*Entity) IsDefaultRequires added in v0.17.94

func (e *Entity) IsDefaultRequires() bool

IsDefaultRequires reports whether @requires uses the default (post-resolver unmarshal) strategy.

func (*Entity) IsExplicitRequires added in v0.17.94

func (e *Entity) IsExplicitRequires() bool

IsExplicitRequires reports whether @requires uses a user Populate function.

func (*Entity) IsPreloaded added in v0.17.94

func (e *Entity) IsPreloaded() bool

IsPreloaded reports whether @requires is populated onto the resolver input representation before the resolver runs.

func (*Entity) NonFederationDirectives added in v0.17.87

func (e *Entity) NonFederationDirectives() ast.DirectiveList

NonFederationDirectives returns the AST directives applied to this entity, excluding federation-internal and gqlgen-internal directives.

func (*Entity) PerEntityResolverName added in v0.17.96

func (e *Entity) PerEntityResolverName() string

PerEntityResolverName is the GraphQL field name of the entity's per-entity resolver; the Go method name is its ToGo form.

type EntityResolver added in v0.15.0

type EntityResolver struct {
	ResolverName   string      // The resolver name, such as FindUserByID
	KeyFields      []*KeyField // The fields declared in @key.
	InputType      types.Type  // The Go generated input type for multi entity resolvers
	InputTypeName  string
	ReturnType     types.Type // The Go generated return type for the entity
	ReturnTypeName string
}

func (*EntityResolver) FieldSet added in v0.17.96

func (e *EntityResolver) FieldSet() string

FieldSet returns the resolver's @key field set in a standard form: fields in key order, separated by single spaces, a nested selection in braces after its field, such as "id owner { id name }".

func (*EntityResolver) IsPointerReturnType added in v0.17.87

func (e *EntityResolver) IsPointerReturnType() bool

IsPointerReturnType returns true if the resolver's return type is a pointer

func (*EntityResolver) LookupInputType added in v0.17.28

func (e *EntityResolver) LookupInputType() string

func (*EntityResolver) LookupReturnType added in v0.17.87

func (e *EntityResolver) LookupReturnType() string

type Federation added in v0.17.54

type Federation struct {
	Entities         []*Entity
	RequiresEntities map[string]*Entity
	PackageOptions   PackageOptions
	// contains filtered or unexported fields
}

func New

func New(version int, cfg *config.Config) (*Federation, error)

New returns a federation plugin that injects federated directives and types into the schema

func (*Federation) GenerateCode added in v0.17.54

func (f *Federation) GenerateCode(data *codegen.Data) error

func (*Federation) InjectSourcesEarly added in v0.17.54

func (f *Federation) InjectSourcesEarly() ([]*ast.Source, error)

func (*Federation) InjectSourcesLate added in v0.17.54

func (f *Federation) InjectSourcesLate(schema *ast.Schema) ([]*ast.Source, error)

InjectSourcesLate creates a GraphQL Entity type with all the fields that had the @key directive

func (*Federation) MutateConfig added in v0.17.54

func (f *Federation) MutateConfig(cfg *config.Config) error

MutateConfig mutates the configuration

func (*Federation) Name added in v0.17.54

func (f *Federation) Name() string

Name returns the plugin name

type KeyField

type KeyField struct {
	Definition *ast.FieldDefinition
	Field      fieldset.Field        // len > 1 for nested fields
	Type       *config.TypeReference // The Go representation of that field type
	// GoName is the field name this key takes in the generated multi-resolver
	// input struct. It is normally Field.ToGo(), but is disambiguated with a
	// numeric suffix when two key paths in the same resolver would otherwise
	// produce the same Go name (e.g. "id" and "i { d }" both yield "ID").
	// Using a single stored name keeps the SDL input field, the modelgen struct
	// field, and the template's struct literal in agreement.
	GoName string
}

func (*KeyField) ArgName added in v0.17.96

func (k *KeyField) ArgName() string

ArgName is the argument name this key takes on a single entity resolver.

type PackageOptions added in v0.17.54

type PackageOptions struct {
	// ExplicitRequires will generate a function in the execution context
	// to populate fields using the @required directive into the entity.
	//
	// You can only set one of ExplicitRequires or ComputedRequires to true.
	ExplicitRequires bool
	// ComputedRequires generates resolver functions to compute values for
	// fields using the @required directive.
	ComputedRequires bool
	// EntityResolverMulti is default engine for entityResolver generation.
	// This can be overriding by @entityResolver(multi: Boolean) directive.
	// false by default.
	EntityResolverMulti bool
	// EntityResolverPerEntity generates one resolver per entity, taking a
	// representation input, instead of one per @key, for every entity. A
	// temporary opt-in: it is planned to become the default. false by default.
	EntityResolverPerEntity bool
	// PreloadedRequires sets "preloaded" as the package-level default @requires
	// strategy; individual entities may override it via
	// @entityResolver(requires: "…"). Under preloaded, the generated multi
	// resolver input (<Entity>By<Keys>sInput) carries the entity's @requires
	// fields, unmarshaled before the resolver runs, so a multi resolver sees
	// every entity's @requires data in one scope — enabling a naturally-batched
	// computation (e.g. one ML-inference call across the whole batch). Multi
	// entities only, unless EntityResolverPerEntity is set: then the
	// <Type>Representation of every entity, single or multi, carries them,
	// nullable, nested and list paths included. false by default.
	PreloadedRequires bool
}

type Requires

type Requires struct {
	Name  string                // the name of the field
	Field fieldset.Field        // source Field, len > 1 for nested fields
	Type  *config.TypeReference // The Go representation of that field type
	// Computed reports whether this @requires field is delivered via a standalone
	// field resolver (the computed strategy) rather than through the entity
	// resolver. It is true when the field carries @computedRequires, or when the whole
	// entity resolves to RequiresComputed (the computed_requires package option,
	// which computes every @requires field). Set in buildRequires.
	Computed bool
}

Requires represents an @requires clause

type RequiresStrategy added in v0.17.94

type RequiresStrategy string

RequiresStrategy selects how an entity's @requires fields are delivered to its resolver. Three strategies target the entity resolver and form a single axis — WHERE @requires data is handed over, relative to the resolver call — and are selected per entity via @entityResolver(requires: "…"), falling back to the package-level default:

  • RequiresDefault ("default"): unmarshaled onto the returned entity AFTER the resolver runs (output, after).
  • RequiresExplicit ("explicit"): handed to a user-implemented Populate<Entity>Requires hook as the raw representation map, AFTER the resolver runs (output, after — user-owned). This is the only strategy that surfaces the raw representation to user code.
  • RequiresPreloaded ("preloaded"): unmarshaled onto the resolver's INPUT representation BEFORE the resolver runs, so a multi resolver sees every entity's @requires data in one scope (input, before). Multi entities only, unless per-entity resolvers take the representation.

RequiresComputed ("computed") is the outlier: it does not touch the entity resolver at all. It routes @requires to standalone field resolvers via a federationRequires argument (Federation 2 only), so it is off the axis the directive selects on — it is therefore NOT a @entityResolver(requires:) value. It is selected either per package (computed_requires, computing every @requires field on its entities) or per field (@computedRequires on FIELD_DEFINITION), which lets one entity compute some @requires fields while delivering the rest through the entity resolver. The per-field flag lives on Requires.Computed. It shares this type because the entity-resolver strategies are mutually exclusive per entity.

The entity-resolver strategies (default/explicit/preloaded) are mutually exclusive: each entity resolves to exactly one.

const (
	// RequiresDefault unmarshals @requires onto the returned entity after the
	// resolver runs.
	RequiresDefault RequiresStrategy = "default"
	// RequiresExplicit delegates @requires population to a user-implemented
	// Populate<Entity>Requires function, called after the resolver.
	RequiresExplicit RequiresStrategy = "explicit"
	// RequiresComputed delivers @requires to standalone field resolvers
	// (Federation 2 only). Selected by the computed_requires package option,
	// not by @entityResolver(requires:).
	RequiresComputed RequiresStrategy = "computed"
	// RequiresPreloaded unmarshals @requires onto the resolver's input
	// representation before the resolver runs, so a multi resolver sees every
	// entity's @requires data at once. Multi entities only, unless per-entity
	// resolvers take the representation.
	RequiresPreloaded RequiresStrategy = "preloaded"
)

Directories

Path Synopsis
test_data

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL