github.com/operandinc/gqlgen@v0.16.1/docs/content/reference/scalars.md (about) 1 --- 2 linkTitle: Scalars 3 title: Mapping GraphQL scalar types to Go types 4 description: Mapping GraphQL scalar types to Go types 5 menu: { main: { parent: "reference", weight: 10 } } 6 --- 7 8 ## Built-in helpers 9 10 gqlgen ships with some built-in helpers for common custom scalar use-cases, `Time`, `Any`, `Upload` and `Map`. Adding any of these to a schema will automatically add the marshalling behaviour to Go types. 11 12 ### Time 13 14 ```graphql 15 scalar Time 16 ``` 17 18 Maps a `Time` GraphQL scalar to a Go `time.Time` struct. This scalar adheres to the [time.RFC3339Nano](https://pkg.go.dev/time#pkg-constants) format. 19 20 ### Map 21 22 ```graphql 23 scalar Map 24 ``` 25 26 Maps an arbitrary GraphQL value to a `map[string]interface{}` Go type. 27 28 ### Upload 29 30 ```graphql 31 scalar Upload 32 ``` 33 34 Maps a `Upload` GraphQL scalar to a `graphql.Upload` struct, defined as follows: 35 36 ```go 37 type Upload struct { 38 File io.Reader 39 Filename string 40 Size int64 41 ContentType string 42 } 43 ``` 44 45 ### Any 46 47 ```graphql 48 scalar Any 49 ``` 50 51 Maps an arbitrary GraphQL value to a `interface{}` Go type. 52 53 ## Custom scalars with user defined types 54 55 For user defined types you can implement the [graphql.Marshaler](https://pkg.go.dev/github.com/operandinc/gqlgen/graphql#Marshaler) and [graphql.Unmarshaler](https://pkg.go.dev/github.com/operandinc/gqlgen/graphql#Unmarshaler) or implement the [graphql.ContextMarshaler](https://pkg.go.dev/github.com/operandinc/gqlgen/graphql#ContextMarshaler) and [graphql.ContextUnmarshaler](https://pkg.go.dev/github.com/operandinc/gqlgen/graphql#ContextUnmarshaler) interfaces and they will be called. 56 57 ```go 58 package mypkg 59 60 import ( 61 "context" 62 "fmt" 63 "io" 64 "strconv" 65 ) 66 67 // 68 // Most common scalars 69 // 70 71 type YesNo bool 72 73 // UnmarshalGQL implements the graphql.Unmarshaler interface 74 func (y *YesNo) UnmarshalGQL(v interface{}) error { 75 yes, ok := v.(string) 76 if !ok { 77 return fmt.Errorf("YesNo must be a string") 78 } 79 80 if yes == "yes" { 81 *y = true 82 } else { 83 *y = false 84 } 85 return nil 86 } 87 88 // MarshalGQL implements the graphql.Marshaler interface 89 func (y YesNo) MarshalGQL(w io.Writer) { 90 if y { 91 w.Write([]byte(`"yes"`)) 92 } else { 93 w.Write([]byte(`"no"`)) 94 } 95 } 96 97 // 98 // Scalars that need access to the request context 99 // 100 101 type Length float64 102 103 // UnmarshalGQLContext implements the graphql.ContextUnmarshaler interface 104 func (l *Length) UnmarshalGQLContext(ctx context.Context, v interface{}) error { 105 s, ok := v.(string) 106 if !ok { 107 return fmt.Errorf("Length must be a string") 108 } 109 length, err := ParseLength(s) 110 if err != nil { 111 return err 112 } 113 *l = length 114 return nil 115 } 116 117 // MarshalGQLContext implements the graphql.ContextMarshaler interface 118 func (l Length) MarshalGQLContext(ctx context.Context, w io.Writer) error { 119 s, err := l.FormatContext(ctx) 120 if err != nil { 121 return err 122 } 123 w.Write([]byte(strconv.Quote(s))) 124 return nil 125 } 126 127 // ParseLength parses a length measurement string with unit on the end (eg: "12.45in") 128 func ParseLength(string) (Length, error) 129 130 // ParseLength formats the string using a value in the context to specify format 131 func (l Length) FormatContext(ctx context.Context) (string, error) 132 ``` 133 134 and then wire up the type in .gqlgen.yml or via directives like normal: 135 136 ```yaml 137 models: 138 YesNo: 139 model: github.com/me/mypkg.YesNo 140 ``` 141 142 ## Custom scalars with third party types 143 144 Sometimes you are unable to add add methods to a type - perhaps you don't own the type, or it is part of the standard 145 library (eg string or time.Time). To support this we can build an external marshaler: 146 147 ```go 148 package mypkg 149 150 import ( 151 "fmt" 152 "io" 153 "strings" 154 155 "github.com/operandinc/gqlgen/graphql" 156 ) 157 158 159 func MarshalMyCustomBooleanScalar(b bool) graphql.Marshaler { 160 return graphql.WriterFunc(func(w io.Writer) { 161 if b { 162 w.Write([]byte("true")) 163 } else { 164 w.Write([]byte("false")) 165 } 166 }) 167 } 168 169 func UnmarshalMyCustomBooleanScalar(v interface{}) (bool, error) { 170 switch v := v.(type) { 171 case string: 172 return "true" == strings.ToLower(v), nil 173 case int: 174 return v != 0, nil 175 case bool: 176 return v, nil 177 default: 178 return false, fmt.Errorf("%T is not a bool", v) 179 } 180 } 181 ``` 182 183 Then in .gqlgen.yml point to the name without the Marshal|Unmarshal in front: 184 185 ```yaml 186 models: 187 MyCustomBooleanScalar: 188 model: github.com/me/mypkg.MyCustomBooleanScalar 189 ``` 190 191 **Note:** you also can un/marshal to pointer types via this approach, simply accept a pointer in your 192 `Marshal...` func and return one in your `Unmarshal...` func. 193 194 **Note:** you can also un/marshal with a context by having your custom marshal function return a 195 `graphql.ContextMarshaler` _and_ your unmarshal function take a `context.Context` as the first argument. 196 197 See the [\_examples/scalars](https://github.com/operandinc/gqlgen/tree/master/_examples/scalars) package for more examples. 198 199 ## Marshaling/Unmarshaling Errors 200 201 The errors that occur as part of custom scalar marshaling/unmarshaling will return a full path to the field. 202 For example, given the following schema ... 203 204 ```graphql 205 extend type Mutation { 206 updateUser(userInput: UserInput!): User! 207 } 208 209 input UserInput { 210 name: String! 211 primaryContactDetails: ContactDetailsInput! 212 secondaryContactDetails: ContactDetailsInput! 213 } 214 215 scalar Email 216 input ContactDetailsInput { 217 email: Email! 218 } 219 ``` 220 221 ... and the following variables: 222 223 ```json 224 { 225 "userInput": { 226 "name": "George", 227 "primaryContactDetails": { 228 "email": "not-an-email" 229 }, 230 "secondaryContactDetails": { 231 "email": "george@gmail.com" 232 } 233 } 234 } 235 ``` 236 237 ... and an unmarshal function that returns an error if the email is invalid. The mutation will return an error containing the full path: 238 239 ```json 240 { 241 "message": "email invalid", 242 "path": ["updateUser", "userInput", "primaryContactDetails", "email"] 243 } 244 ``` 245 246 **Note:** Marshaling errors can only be returned when using the `graphql.ContextMarshaler` style interface.