embedded_interfaces
Embedding Interfaces
[edit]
master branch. The latest version is v0.17.83.
Embedding a GraphQL interface in a Go struct lets you share behavior without duplicating fields. Mark the interface with
@goEmbedInterface and gqlgen would generate Base{Interface} struct and embed it to all implementors instead of copying fields.
@goEmbedInterface get Base{Interface} structs generated for embedding.A & B & C keeps a single copy of shared parents. When some intermediate interfaces are not annotated, their fields are included directly in the next implementor struct.directive @goEmbedInterface on INTERFACE
type Query {
product(id: ID!): Product!
}
interface Node @goEmbedInterface {
id: ID!
}
interface Ownable @goEmbedInterface {
owner: String!
}
type Product implements Node & Ownable {
id: ID!
owner: String!
name: String!
}
Configuration:
# gqlgen.yml
schema:
- schema.graphqls
model:
filename: graph/model/models_gen.go
gqlgen generates BaseNode and BaseOwnable structs (only for interfaces with @goEmbedInterface), and Product embeds both:
type BaseNode struct {
ID string `json:"id"`
}
func (BaseNode) IsNode() {}
type BaseOwnable struct {
Owner string `json:"owner"`
}
func (BaseOwnable) IsOwnable() {}
type Product struct {
BaseNode
BaseOwnable
Name string `json:"name"`
}
func (Product) IsNode() {}
func (Product) IsOwnable() {}
In resolvers, call methods that return base implementations—no need to construct interface fields manually:
func (r *queryResolver) Product(ctx context.Context, id string) (*model.Product, error) {
node, err := r.productService.GetNode(ctx, id)
if err != nil {
return nil, err
}
owner, err := r.productService.GetOwner(ctx, id)
if err != nil {
return nil, err
}
name, err := r.productService.GetName(ctx, id)
if err != nil {
return nil, err
}
return &model.Product{
BaseNode: node, // embeds ID from service
BaseOwnable: owner, // embeds Owner from service
Name: name,
}, nil
}
Limitations
@goEmbedInterface, both base structs are embedded. This works correctly as long as there are no field name conflicts, which prevents embedding of both parents.data: NodeData! but implementation uses data: ProductNodeData!), gqlgen skips embedding the base struct for that interface and generates explicit fields instead.