graphql-nexus / nexus

Code-First, Type-Safe, GraphQL Schema Construction
https://nexusjs.org
MIT License
3.4k stars 275 forks source link

Allow multiple sourceType for scalar #784

Open Sytten opened 3 years ago

Sytten commented 3 years ago

Currently the sourceType only allows for one type, it happens that you need to accept multiple types. For example our DateTime scalar accepts the JS Date and luxon DateTime. For now what we do is we created a new type that we use in the global sourceTypes. Ideally it would be nice to have an input type and an output type, but only one is also fine if thats too big of refactor.

jasonkuhrt commented 3 years ago

Can you share some code examples of what you wish the API allowed you to do, and usage examples.

Sytten commented 3 years ago

So this is our implementation of datetime:

import { GraphQLError } from 'graphql/error'
import { Kind } from 'graphql/language'
import { DateTime } from 'luxon'
import { scalarType } from 'nexus'

const validate = (value: any) => {
  if (typeof value !== 'string') {
    throw new TypeError(`Value is not string: ${value}`)
  }
  const dateTime = DateTime.fromISO(value)
  if (!dateTime.isValid) throw new TypeError(`Value is not DateTime: ${value}`)
  return dateTime
}

export const GraphQLDateTime = scalarType({
  asNexusMethod: 'datetime',
  description: 'The `DateTime` scalar type represents an ISO 8601 DateTime',
  name: 'DateTime',
  parseLiteral(ast) {
    if (ast.kind !== Kind.STRING) {
      throw new GraphQLError(`Can only sanitize DateTime strings, but got: ${ast.kind}`)
    }

    return validate(ast.value)
  },
  parseValue: validate,
  serialize: (value: DateTime | Date) => {
    if (value instanceof DateTime) {
      return value.toISO()
    }
    if (value instanceof Date) {
      return value.toISOString()
    }
    throw new TypeError(`Value is not DateTime: ${value}`)
  },
  // sourceType: {
  //   export: 'DateTime',
  //   module: 'luxon',
  // },
})

So currently I cannot type it directly because I can only specify one sourceType for it, but I do accept Date and DateTime because prisma doesn't yet allow us to customize it. This is a bit annoying because in input I always parse as a DateTime so created this little hack:

export const jsDateOrDateTimeToDateTime = (
  input: DateTime | Date | null | undefined
): DateTime | null | undefined =>
  DateTime.isDateTime(input)
    ? input
    : input === null
    ? null
    : input === undefined
    ? undefined
    : DateTime.fromJSDate(input)

Ideally I would like to be able to have a different type for when the scalar is used in the input and when it is used in the output.