The payload
field can be Int
or String
scalar type.
when I write it like union type:
const schema = `
input QuickReply {
content_type: String
title: String
payload: Int | String
image_url: String
}
`
I got an error:
GraphQLError: Syntax Error GraphQL request (45:18) Expected Name, found |
44: title: String
45: payload: Int | String
^
46: image_url: String
It seems GraphQL
does not support union scalar type.
So, how can I solve this situation?
Scalars can't be used as part of unions, since per the specification, unions specifically "represent an object that could be one of a list of GraphQL Object types." Instead, you can use a custom scalar. For example:
const MAX_INT = 2147483647
const MIN_INT = -2147483648
const coerceIntString = (value) => {
if (Array.isArray(value)) {
throw new TypeError(`IntString cannot represent an array value: [${String(value)}]`)
}
if (Number.isInteger(value)) {
if (value < MIN_INT || value > MAX_INT) {
throw new TypeError(`Value is integer but outside of valid range for 32-bit signed integer: ${String(value)}`)
}
return value
}
return String(value)
}
const IntString = new GraphQLScalarType({
name: 'IntString',
serialize: coerceIntString,
parseValue: coerceIntString,
parseLiteral(ast) {
if (ast.kind === Kind.INT) {
return coerceIntString(parseInt(ast.value, 10))
}
if (ast.kind === Kind.STRING) {
return ast.value
}
return undefined
}
})
This code effectively combines the behaviors for both the Int and String types, while still enforcing the range for 32-bit signed integers. However, you could have whatever type coercion behavior you want. Check out the source code to see how the built-in scalars work, or this article for more details around how custom scalars work.
Note that if you're trying to return one of several scalars for an output field, it's possible to utilize a union for the parent type to achieve a similar result. For example, this isn't possible:
type Post {
content: String | Int
}
but you can do the following:
type PostString {
content: String
}
type PostInt {
content: Int
}
union Post = PostString | PostInt
Super interesting answer!!! I couldn't find such a relevant example anywhere else....
That sounds like a good solution for mixing multiple scalars. In my case there is a response of a task, and it may be either scalar (Int, String) or object type. I could use scalar JSON instead of object types, but JSON does not validate internal fields and can't guarantee they are in the response, and not an option. So I ran into the same issue, but it seems that this solution won't work for something like:
union = String | Int | MyTypeOne | MyTypeTwo ... MyTypeTen
. Any suggestions?@Mihail Instead of using a JSON scalar, you could still create a custom scalar with validation specific to your use case baked in. Outside of that, if you're using this for an output type, you can use a union for the parent type as described above.