federation

package
v0.17.94 Latest Latest
Warning

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

Go to latest
Published: Jul 9, 2026 License: MIT Imports: 15 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
	// 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.

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) 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
}

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
	// 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. 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.

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.
	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