v0.17.55
v0.17.55 v0.17.54 v0.17.53 v0.17.52 v0.17.51 v0.17.50 v0.17.49 v0.17.48 v0.17.47 v0.17.46 v0.17.45 v0.17.44 v0.17.43 v0.17.42 v0.17.41 v0.17.40 v0.17.39 v0.17.38 v0.17.37 v0.17.36 master

Scalars

Mapping GraphQL scalar types to Go types
[edit]

Built-in helpers

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.

Time

scalar Time

Maps a Time GraphQL scalar to a Go time.Time struct. This scalar adheres to the time.RFC3339Nano format.

Universally Unique Identifier (UUID)

scalar UUID

This maps a UUID scalar value to a uuid.UUID type.

If you add to gqlgen.yml:

models:
  UUID:
    model:
      - github.com/99designs/gqlgen/graphql.UUID

And then add scalar UUID to schema.graphql

See the _examples/uuid package for more examples.

Map

scalar Map

Maps an arbitrary GraphQL value to a map[string]interface{} Go type.

Upload

scalar Upload

Maps a Upload GraphQL scalar to a graphql.Upload struct, defined as follows:

type Upload struct {
	File        io.ReadSeeker
	Filename    string
	Size        int64
	ContentType string
}

Any

scalar Any

Maps an arbitrary GraphQL value to a interface{} Go type.

Duration

scalar Duration

This maps a Duration scalar value conforming to the ISO8601 standard (ex.: P1Y2D) to a time.Duration type.

If you add to gqlgen.yml:

models:
  Duration:
    model:
      - github.com/99designs/gqlgen/graphql.Duration

And then add scalar Duration to schema.graphql

Custom scalars with user defined types

For user defined types you can implement the graphql.Marshaler and graphql.Unmarshaler or implement the graphql.ContextMarshaler and graphql.ContextUnmarshaler interfaces and they will be called.

package mypkg

import (
	"context"
	"fmt"
	"io"
	"strconv"
)

//
// Most common scalars
//

type YesNo bool

// UnmarshalGQL implements the graphql.Unmarshaler interface
func (y *YesNo) UnmarshalGQL(v interface{}) error {
	yes, ok := v.(string)
	if !ok {
		return fmt.Errorf("YesNo must be a string")
	}

	if yes == "yes" {
		*y = true
	} else {
		*y = false
	}
	return nil
}

// MarshalGQL implements the graphql.Marshaler interface
func (y YesNo) MarshalGQL(w io.Writer) {
	if y {
		w.Write([]byte(`"yes"`))
	} else {
		w.Write([]byte(`"no"`))
	}
}

//
// Scalars that need access to the request context
//

type Length float64

// UnmarshalGQLContext implements the graphql.ContextUnmarshaler interface
func (l *Length) UnmarshalGQLContext(ctx context.Context, v interface{}) error {
	s, ok := v.(string)
	if !ok {
		return fmt.Errorf("Length must be a string")
	}
	length, err := ParseLength(s)
	if err != nil {
		return err
	}
	*l = length
	return nil
}

// MarshalGQLContext implements the graphql.ContextMarshaler interface
func (l Length) MarshalGQLContext(ctx context.Context, w io.Writer) error {
	s, err := l.FormatContext(ctx)
	if err != nil {
		return err
	}
	w.Write([]byte(strconv.Quote(s)))
	return nil
}

// ParseLength parses a length measurement string with unit on the end (eg: "12.45in")
func ParseLength(string) (Length, error)

// ParseLength formats the string using a value in the context to specify format
func (l Length) FormatContext(ctx context.Context) (string, error)

and then wire up the type in .gqlgen.yml or via directives like normal:

models:
  YesNo:
    model: github.com/me/mypkg.YesNo

Custom scalars with third party types

Sometimes you are unable to add methods to a type — perhaps you don’t own the type, or it is part of the standard library (eg string or time.Time). To support this we can build an external marshaler:

package mypkg

import (
	"fmt"
	"io"
	"strings"

	"github.com/99designs/gqlgen/graphql"
)


func MarshalMyCustomBooleanScalar(b bool) graphql.Marshaler {
	return graphql.WriterFunc(func(w io.Writer) {
		if b {
			w.Write([]byte("true"))
		} else {
			w.Write([]byte("false"))
		}
	})
}

func UnmarshalMyCustomBooleanScalar(v interface{}) (bool, error) {
	switch v := v.(type) {
	case string:
		return "true" == strings.ToLower(v), nil
	case int:
		return v != 0, nil
	case bool:
		return v, nil
	default:
		return false, fmt.Errorf("%T is not a bool", v)
	}
}

Then in .gqlgen.yml point to the name without the Marshal|Unmarshal in front:

models:
  MyCustomBooleanScalar:
    model: github.com/me/mypkg.MyCustomBooleanScalar

Note: You also can (un)marshal to pointer types via this approach, simply accept a pointer in your Marshal... func and return one in your Unmarshal... func.

Note: You can also (un)marshal with a context by having your custom marshal function return a graphql.ContextMarshaler and your unmarshal function take a context.Context as the first argument.

See the _examples/scalars package for more examples.

Marshaling/Unmarshaling Errors

The errors that occur as part of custom scalar marshaling/unmarshaling will return a full path to the field. For example, given the following schema:

extend type Mutation{
    updateUser(userInput: UserInput!): User!
}

input UserInput {
    name: String!
    primaryContactDetails: ContactDetailsInput!
    secondaryContactDetails: ContactDetailsInput!
}

scalar Email

input ContactDetailsInput {
    email: Email!
}

… and the following variables:

{
  "userInput": {
    "name": "George",
    "primaryContactDetails": {
      "email": "not-an-email"
    },
    "secondaryContactDetails": {
      "email": "[email protected]"
    }
  }
}

… and an unmarshal function that returns an error if the email is invalid. The mutation will return an error containing the full path:

{
  "message": "email invalid",
  "path": [
    "updateUser",
    "userInput",
    "primaryContactDetails",
    "email"
  ]
}

Note: Marshaling errors can only be returned when using the graphql.ContextMarshaler style interface.