Skip to content

Commit

Permalink
Add recipe for using the modelgen FieldMutateHook
Browse files Browse the repository at this point in the history
  • Loading branch information
tprebs authored Oct 8, 2021
1 parent 10a9dea commit b6febf0
Showing 1 changed file with 87 additions and 0 deletions.
87 changes: 87 additions & 0 deletions docs/content/recipes/modelgen-hook.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ linkTitle: "Modelgen hook"
menu: { main: { parent: 'recipes' } }
---

## BuildMutateHook

The following recipe shows how to use a `modelgen` plugin hook to mutate generated
models before they are rendered into a resulting file. This feature has many uses but
the example focuses only on inserting ORM-specific tags into generated struct fields. This
Expand Down Expand Up @@ -77,3 +79,88 @@ type Object struct {
field2 *int `json:"field2" orm_binding:"Object.field2"`
}
```

## FieldMutateHook

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.

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.

``` go
import (
"fmt"
"github.com/vektah/gqlparser/v2/ast"
"os"

"github.com/99designs/gqlgen/api"
"github.com/99designs/gqlgen/codegen/config"
"github.com/99designs/gqlgen/plugin/modelgen"
)

// Defining mutation function
func constraintFieldHook(td *ast.Definition, fd *ast.FieldDefinition, f *modelgen.Field) (*modelgen.Field, error) {

c := fd.Directives.ForName("constraint")
if c != nil {
formatConstraint := c.Arguments.ForName("format")

if formatConstraint != nil{
f.Tag += " validate:"+formatConstraint.Value.String()
}

}

return f, nil
}

func main() {
cfg, err := config.LoadConfigFromDefaultLocations()
if err != nil {
fmt.Fprintln(os.Stderr, "failed to load config", err.Error())
os.Exit(2)
}

// Attaching the mutation function onto modelgen plugin
p := modelgen.Plugin{
FieldHook: constraintFieldHook,
}

err = api.Generate(cfg,
api.NoPlugins(),
api.AddPlugin(&p),
)
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(3)
}
}
```

This schema:

```graphql
directive @constraint(
format: String
) on INPUT_FIELD_DEFINITION | FIELD_DEFINITION

input ObjectInput {
contactEmail: String @constraint(format: "email")
website: String @constraint(format: "uri")
}
```

Will generate the model:

```go
type ObjectInput struct {
contactEmail *string `json:"contactEmail" validate:"email"`
website *string `json:"website" validate:"uri"`
}
```

If a constraint being used during generation shoud not be published during introspection, the directive should be listed with `skip_runtime:true` in gqlgen.yml
```yaml
directives:
constraint:
skip_runtime: true
```

0 comments on commit b6febf0

Please sign in to comment.