diff --git a/_examples/enum/model/model.go b/_examples/enum/model/model.go index b7829c6ba4a..471731a28df 100644 --- a/_examples/enum/model/model.go +++ b/_examples/enum/model/model.go @@ -1,5 +1,14 @@ package model +import ( + "encoding/json" + "fmt" +) + +// this file is provided as an example for int-based enums +// but if you instead wanted to support arbitrary +// english words for numbers to integers, consider +// github.com/will-lol/numberconverter/etoi or a similar library type IntTyped int const ( @@ -12,6 +21,40 @@ const ( IntUntypedTwo ) +func (t IntTyped) String() string { + switch t { + case IntTypedOne: + return "ONE" + case IntTypedTwo: + return "TWO" + default: + return "UNKNOWN" + } +} + +func (t IntTyped) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, t.String())), nil +} + +func (t *IntTyped) UnmarshalJSON(b []byte) (err error) { + var s string + + if err = json.Unmarshal(b, &s); err != nil { + return err + } + + switch s { + case "ONE": + *t = IntTypedOne + case "TWO": + *t = IntTypedTwo + default: + return fmt.Errorf("unexpected enum value %q", s) + } + + return nil +} + type StringTyped string const ( diff --git a/docs/content/recipes/enum.md b/docs/content/recipes/enum.md index dcf4d491bca..314aa46d341 100644 --- a/docs/content/recipes/enum.md +++ b/docs/content/recipes/enum.md @@ -15,6 +15,8 @@ Both typed and untyped binding are supported. More examples can be found in [_examples/enum](https://github.com/99designs/gqlgen/tree/master/_examples/enum). +## Binding Targets + Binding target go model enums: ```golang @@ -80,4 +82,47 @@ models: value: ./model.EnumUntypedOne TWO: value: ./model.EnumUntypedTwo -``` \ No newline at end of file +``` + +## Additional Notes for int-based Enums + +If you want to use the generated input structs that use int-based enums to query your GraphQL server, you need an additional step to convert the int-based enum value into a JSON string representation. Otherwise, most client libraries will send an integer value, which the server will not understand, since it is expecting the string representation (e.g. `ONE` in the above example). + +Therefore, we must implement `MarshalJSON` and `UnmarshalJSON` on the typed enum type to convert between both. This is only possible with typed bindings. + +```go +func (t EnumTyped) String() string { + switch t { + case EnumTypedOne: + return "ONE" + case EnumTypedTwo: + return "TWO" + default: + return "UNKNOWN" + } +} + +func (t EnumTyped) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, t.String())), nil +} + +func (t *EnumTyped) UnmarshalJSON(b []byte) (err error) { + var s string + + if err = json.Unmarshal(b, &s); err != nil { + return err + } + + switch s { + case "ONE": + *t = EnumTypedOne + case "TWO": + *t = EnumTypedTwo + default: + return fmt.Errorf("unexpected enum value %q", s) + } + + return nil +} +``` +