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.