github.com/operandinc/gqlgen@v0.16.1/docs/content/recipes/modelgen-hook.md (about)

     1  ---
     2  title: "Allowing mutation of generated models before rendering"
     3  description: How to use a model mutation function to insert a ORM-specific tags onto struct fields.
     4  linkTitle: "Modelgen hook"
     5  menu: { main: { parent: "recipes" } }
     6  ---
     7  
     8  ## BuildMutateHook
     9  
    10  The following recipe shows how to use a `modelgen` plugin hook to mutate generated
    11  models before they are rendered into a resulting file. This feature has many uses but
    12  the example focuses only on inserting ORM-specific tags into generated struct fields. This
    13  is a common use case since it allows for better field matching of DB queries and
    14  the generated data structure.
    15  
    16  First of all, we need to create a function that will mutate the generated model.
    17  Then we can attach the function to the plugin and use it like any other plugin.
    18  
    19  ```go
    20  import (
    21  	"fmt"
    22  	"os"
    23  
    24  	"github.com/operandinc/gqlgen/api"
    25  	"github.com/operandinc/gqlgen/codegen/config"
    26  	"github.com/operandinc/gqlgen/plugin/modelgen"
    27  )
    28  
    29  // Defining mutation function
    30  func mutateHook(b *modelgen.ModelBuild) *modelgen.ModelBuild {
    31  	for _, model := range b.Models {
    32  		for _, field := range model.Fields {
    33  			field.Tag += ` orm_binding:"` + model.Name + `.` +  field.Name + `"`
    34  		}
    35  	}
    36  
    37  	return b
    38  }
    39  
    40  func main() {
    41  	cfg, err := config.LoadConfigFromDefaultLocations()
    42  	if err != nil {
    43  		fmt.Fprintln(os.Stderr, "failed to load config", err.Error())
    44  		os.Exit(2)
    45  	}
    46  
    47  	// Attaching the mutation function onto modelgen plugin
    48  	p := modelgen.Plugin{
    49  		MutateHook: mutateHook,
    50  	}
    51  
    52  	err = api.Generate(cfg, api.ReplacePlugin(&p))
    53  
    54  	if err != nil {
    55  		fmt.Fprintln(os.Stderr, err.Error())
    56  		os.Exit(3)
    57  	}
    58  }
    59  ```
    60  
    61  Now fields from generated models will contain a additional tag `orm_binding`.
    62  
    63  This schema:
    64  
    65  ```graphql
    66  type Object {
    67  	field1: String
    68  	field2: Int
    69  }
    70  ```
    71  
    72  Will gen generated into:
    73  
    74  ```go
    75  type Object struct {
    76  	field1 *string  `json:"field1" orm_binding:"Object.field1"`
    77  	field2 *int     `json:"field2" orm_binding:"Object.field2"`
    78  }
    79  ```
    80  
    81  ## FieldMutateHook
    82  
    83  For more fine grained control over model generation, a graphql schema aware a FieldHook can be provided. This hook has access to type and field graphql definitions enabling the hook to modify the `modelgen.Field` using directives defined within the schema.
    84  
    85  The below recipe uses this feature to add validate tags to the generated model for use with `go-playground/validator` where the validate tags are defined in a constraint directive in the schema.
    86  
    87  ```go
    88  import (
    89  	"fmt"
    90  	"github.com/vektah/gqlparser/v2/ast"
    91  	"os"
    92  
    93  	"github.com/operandinc/gqlgen/api"
    94  	"github.com/operandinc/gqlgen/codegen/config"
    95  	"github.com/operandinc/gqlgen/plugin/modelgen"
    96  )
    97  
    98  // Defining mutation function
    99  func constraintFieldHook(td *ast.Definition, fd *ast.FieldDefinition, f *modelgen.Field) (*modelgen.Field, error) {
   100  
   101  	c := fd.Directives.ForName("constraint")
   102  	if c != nil {
   103  		formatConstraint := c.Arguments.ForName("format")
   104  
   105  		if formatConstraint != nil{
   106  			f.Tag += " validate:"+formatConstraint.Value.String()
   107  		}
   108  
   109  	}
   110  
   111  	return f, nil
   112  }
   113  
   114  func main() {
   115  	cfg, err := config.LoadConfigFromDefaultLocations()
   116  	if err != nil {
   117  		fmt.Fprintln(os.Stderr, "failed to load config", err.Error())
   118  		os.Exit(2)
   119  	}
   120  
   121  	// Attaching the mutation function onto modelgen plugin
   122  	p := modelgen.Plugin{
   123  		FieldHook: constraintFieldHook,
   124  	}
   125  
   126  	err = api.Generate(cfg, api.ReplacePlugin(&p))
   127  
   128  	if err != nil {
   129  		fmt.Fprintln(os.Stderr, err.Error())
   130  		os.Exit(3)
   131  	}
   132  }
   133  ```
   134  
   135  This schema:
   136  
   137  ```graphql
   138  directive @constraint(
   139  	format: String
   140  ) on INPUT_FIELD_DEFINITION | FIELD_DEFINITION
   141  
   142  input ObjectInput {
   143  	contactEmail: String @constraint(format: "email")
   144  	website: String @constraint(format: "uri")
   145  }
   146  ```
   147  
   148  Will generate the model:
   149  
   150  ```go
   151  type ObjectInput struct {
   152  	contactEmail *string  `json:"contactEmail" validate:"email"`
   153  	website      *string  `json:"website" validate:"uri"`
   154  }
   155  ```
   156  
   157  If a constraint being used during generation should not be published during introspection, the directive should be listed with `skip_runtime:true` in gqlgen.yml
   158  
   159  ```yaml
   160  directives:
   161    constraint:
   162      skip_runtime: true
   163  ```
   164  
   165  The built-in directive `@goTag` is implemented using the FieldMutateHook. See: `plugin/modelgen/models.go` function `GoTagFieldHook`