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