Search code examples
graphqlapolloapollo-clientapollo-server

Type should reference a specific enum or union using Apollo Graphql


I'm trying to create what I thought would be a Union or Enum for Apollo GraphQL, however, my understanding is at noob level. What is the correct way to write the code below.

Path: sampleType.graphql

union GridSize = 'auto' | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;

type Element {
  xs: GridSize
}

Solution

  • GraphQL doesn't support string literals or numerical literals like you're doing. In TypeScript, for example, you can use literals like that:

    type GridSize = 'auto' | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12
    

    GraphQL doesn't have anything like that. You can use enums:

    enum GRID_SIZE {
      SOME_VALUE_HERE
      SECOND_OPTION
    }
    

    but unfortunately, your example won't work because the values in enums have to follow this regular expression /[_A-Za-z][_0-9A-Za-z]*/, which means it can't start with (or be only) a number.


    So what can you do?

    You can use Dani R's commented answer, and just create a union that accepts an Int | String. This is the easiest thing. Alternatively, if you want to make this its own type "for real" – which acts the way you're describing it (mostly) – you can create a custom scalar:

    """
    The following values are accepted
    'auto' | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12
    """
    scalar GridSize
    
    type Element {
      xs: GridSize
    }
    

    Then you need to create your scalar resolvers:

    const { GraphQLScalarType, Kind } = require('graphql')
    const allowedValues = ['auto', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    
    const gridSizeScalar = new GraphQLScalarType({
      name: 'GridSize',
      description: `
      The following values are accepted
      'auto' | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12
      `,
      serialize(value) {
        // outgoing from your code
        if (!allowedValues.includes(value)) {
          // or you could throw, but your own code had better not return bad values :)
          console.error('something')
          return null
        }
        return value
      },
      parseValue(value) {
        // incoming from input
        if (!allowedValues.includes(value)) {
          // you probably have a better error message than I do
          throw new RangeError('Not a valid Grid Size')
        }
        return value
      },
      parseLiteral(ast) {
        if ((ast.kind !== Kind.INT) && (ast.kind !== Kind.STRING)) {
          // if this value isn't a string or number, it's definitely not OK
          return null
        }
        return ast.value
      }
    })
    

    and just stick that in your resolvers

    const resolvers = {
      Query: {},
      Mutation: {},
      Whatever: {},
      GridSize: gridSizeScalar
    }