Describe the bug
Currently the way type-graphql, graphql and class-validator treats enums leads to some confusing behavior:
GraphQL only recognizes one value in enums, not a key-value pair like TypeScript does. type-graphql chose to use the key of an enum in the graphql representation. The validation logic of class-validator validates for the value, though. This leads to the situation that if you request the value of an enum from graphql in a query and then send it back in a mutation, it will fail validation.
To Reproduce
Server side
enum ExampleEnum {
Foo = '1'
Bar = '2'
}
registerEnumType(ExampleEnum), {
name: 'ExampleEnum'
})
@ObjectType()
export class ExampleType {
@Field(type => ExampleEnum)
@IsEnum(ExampleEnum)
public fooOrBar: ExampleEnum
}
@Resolver(ExampleType)
class ExampleResolver {
@Mutation(type => ExampleType)
public async updateExampleType (@Arg('fooOrBar', type => ExampleEnum) fooOrBar: ExampleEnum) {
return { fooOrBar }
}
}
Connect this to e. g. Apollo and then the following test
const query = `mutation { updateExampleType(fooOrBar: ${ExampleEnum.Foo}) { fooOrBar } }`
server.post('/graphql')
.set('Accept', 'application/json')
.send({ query })
.expect(200)
will fail with a validation error because it sends in fooOrBar: 1 but expects fooOrBar: Foo.
Expected behavior
Behavior between validation and the rest of the code should be consistent. The easiest way would be to use enum values instead of enum labels so that fooOrBar: 1 would be the expected value in the code above. This would be a breaking change, so it should be hidden behind a flag, ideally on registerEnumType.
Enviorment (please complete the following information):
i have the same problem
My first and most important question is: why the hell you have @IsEnum(ExampleEnum) on the enum field of GraphQL ObjectType? You don't trust graphql-js and you think that one of 1 million times it will pass an incorrect value? 馃槃
The mechanism of enums in TypeGraphQL is quite simple. GraphQL SDL doesn't have any mapping capabilities, so we describe enum like this:
enum RGB {
RED
BLUE
GREEN
}
So you will receive "RED" string in JSON while querying as output data. When you provide the value as input, you have arg: RED syntax without the quotes and "RED" string as a variable in JSON.
But sometimes to have an idiomatic GraphQL API you need to map from the UPPERCASE enum field names of GraphQL into numbers or other strings, so you declare them in this way in TS code
enum RGB {
RED = "#FF0000"
BLUE = "#0000FF"
GREEN = "#00FF00"
}
This way in your code on runtime you will have the values on the right that correspond to your database values or something.
The easiest way would be to use enum values instead of enum labels
This is against GraphQL Spec:
Enums are not references for a numeric value, but are unique values in their own right. They may serialize as a string: the name of the represented value.
So you will receive "1" string instead of 1 number.
This leads to the situation that if you request the value of an enum from graphql in a query and then send it back in a mutation, it will fail validation.
No, it's not true - you will return "RED" as response and you may pass "RED" as a value in variables JSON.
The validation logic of class-validator validates for the value, though.
So just use Object.keys(ExampleEnum) to have an array of GraphQL enum labels 馃槈
My first and most important question is: why the hell you have
@IsEnum(ExampleEnum)on the enum field of GraphQL ObjectType? You don't trustgraphql-jsand you think that one of 1 million times it will pass an incorrect value? 馃槃
Two reasons:
The easiest way would be to use enum values instead of enum labels
This is against GraphQL Spec:
Enums are not references for a numeric value, but are unique values in their own right. They may serialize as a string: the name of the represented value.
So you will receive
"1"string instead of1number.
Good point, I didn't think about that. This makes the situation even more complicated :-/
This leads to the situation that if you request the value of an enum from graphql in a query and then send it back in a mutation, it will fail validation.
No, it's not true - you will return "RED" as response and you may pass "RED" as a value in variables JSON.
Ok, right again - the problem only appears if the frontend tries to create a new value with the same type that the backend uses.
So just use
Object.keys(ExampleEnum)to have an array of GraphQL enum labels 馃槈
Then the type becomes incompatible with the backend, so this only moves the problem.
So basically, to sum up the enums rules:
"RED" | "GREEN" | "BLUE")enum RGB {
RED = "RED"
BLUE = "BLUE"
GREEN = "GREEN"
}
Then you can use them both on server and client side 馃槈
Ok, makes sense. Would you be interested in a PR adding this to the docs? :)
@dbartholomae
Done 馃槈
https://typegraphql.ml/docs/enums.html#interoperability
How can I set column values to be of an enum? Let say there is a field with the name saftey_features and it has values from enum.
Most helpful comment
@dbartholomae
Done 馃槈
https://typegraphql.ml/docs/enums.html#interoperability