graphql / graphql-spec

GraphQL is a query language and execution engine tied to any backend service.
https://spec.graphql.org
14.26k stars 1.12k forks source link

Proposal: Support union scalar types #215

Open stubailo opened 7 years ago

stubailo commented 7 years ago

It makes sense often to have a field that could return, for example, an Int or a String. It would be convenient to be able to use a resolveType-like mechanism to determine how the value is serialized. This would be even more useful for custom scalars.

The specification currently says: "GraphQL Unions represent an object that could be one of a list of GraphQL Object types", which basically means that unions are the same as interfaces, but without any shared fields. Supporting unions of scalars would further differentiate unions from interfaces.

ericvergnaud commented 4 years ago

Marking this as Strawman and Needs Champion, however I'm still very skeptical since not all target server or client languages support scalar unions.

GraphQL already prevents (via validation rules) other scenarios where a field could return more than one kind of type.

If anyone decides to take ownership of this proposal, I think the bar will be set high to prove value and early attention should be placed on how it would affect code generation systems, especially ones based in Java/C/C++/ObjC.

FWIW I'm planning to submit a PR for the Java implementation. I need unions of enums, and restricting unions to objects really adds no value imho. I don't disagree with some comments preventing against the difficulty of deciding the exact type at runtime, but that problem sits with the schema implementer i.e. if the union results in an ambiguous thus impossible resolution scenario, then they should avoid it in the schema (and the schema parser could detect them at parsing time)

benjie commented 4 years ago

@ericvergnaud I don't think that would pan out well in the longer term. For example:

enum Fruit {
  APPLE
  BANANA
  COCONUT
}

enum Color {
  RED
  GREEN
  BLUE
}

enumUnion FruitOrColor = Fruit | Color

type Query {
  colorRgb(color: Color): String
  fruitCalories(fruit: Fruit): Float
  hue(of: FruitOrColor): Float
}

This works, but as soon as you add Orange to both Fruit and Color things break due to ambiguity, which means you're no longer able to evolve your independent Fruit and Color enums as you'd like to, because they happen to be in the same enumUnion. To evolve your enums you'd have to have kludges like ORANGE_COLOR and ORANGE_FRUIT, which are non-optimal. (Note: this is not the same concern as interfaces because types themselves opt into interfaces via type Foo implements MyInterface.)

ericvergnaud commented 4 years ago

Not sure your example is relevant?

The moment you encounter those naming collisions is precisely when you no longer need a union.

This would be part of the spec and should simply be detected as an invalid construct (it is today within an enum)

I have this use case where the full set of names can be divided into subsets.

I need a named type for each subset, and an umbrella type for the entire set.

GraphQL currently will not let me do that, whilst it is a perfectly legitimate ask.

Envoyé de mon iPhone

Le 12 mai 2020 à 23:39, Benjie Gillam notifications@github.com a écrit :

 @ericvergnaud I don't think that would pan out well in the longer term. For example:

enum Fruit { APPLE BANANA COCONUT }

enum Color { RED GREEN BLUE }

enumUnion FruitOrColor = Fruit | Color

type Query { colorRgb(color: Color): String fruitCalories(fruit: Fruit): Float hue(of: FruitOrColor): Float } This works, but as soon as you add Orange to both Fruit and Color things break due to ambiguity, which means you're no longer able to evolve your independent Fruit and Color enums as you'd like to, because they happen to be in the same enumUnion. To evolve your enums you'd have to have kludges like ORANGE_COLOR and ORANGE_FRUIT, which are non-optimal. (Note: this is not the same concern as interfaces because types themselves opt into interfaces via type Foo implements MyInterface.)

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.

benjie commented 4 years ago

To prefix this comment, please note that I'm one of the people pushing for scalar unions with actual proposed spec edits, so the reason for these questions is so I can fully understand your point of view such that I can consider incorporating it into my proposals.

The moment you encounter those naming collisions is precisely when you no longer need a union.

I don't follow, @ericvergnaud. Surely removing a union at a later stage in your project would be a breaking change, invalidating many previously valid operations involving the hue field?

This would be part of the spec and should simply be detected as an invalid construct (it is today within an enum)

Having two enums that each contain a value with the same name is perfectly valid according to the current GraphQL spec and reference implementation, so again I don't quite follow what you're getting at here.

Note in my previous comment I express my opinion that defining a "scalar union" elsewhere in the schema should not impact on the values I can add to my original enums - these enums don't "know about" the scalar union, so their evolution should be unaffected by it.

I have this use case where the full set of names can be divided into subsets. I need a named type for each subset, and an umbrella type for the entire set. GraphQL currently will not let me do that, whilst it is a perfectly legitimate ask.

It seems to me that if your enum values are all uniquely named, you could create a "union" enum that just contained all of those values, e.g.

enum Fruit {
  FRUIT_APPLE
  FRUIT_BANANA
  FRUIT_COCONUT
}

enum Color {
  COLOR_RED
  COLOR_GREEN
  COLOR_BLUE
}

enum FruitColorUnion {
  FRUIT_APPLE
  FRUIT_BANANA
  FRUIT_COCONUT
  COLOR_RED
  COLOR_GREEN
  COLOR_BLUE
}

Is there a reason this is not a suitable solution to your issue? If so, please could you expand? If I've misunderstood, please could you demonstrate with an example?


To try and make this discussion more concrete, here's an example schema where ORANGE is used in two different enums with two different meanings:

https://codesandbox.io/s/elated-night-i776t

Could you explain within the context of this schema?

ericvergnaud commented 4 years ago

1) what I mean is that if you start seeing naming collisions, it’s about time you revisit your data model. Indeed it will break things, not sure why that’s a problem if it’s required? 2) the spec would explicitly state that union AB = A | B is illegal if A and B share names 3) sorry but I disagree with your principle that : these enums don't "know about" the scalar union, so their evolution should be unaffected by it. A schema is not a bunch of independent names, rather it’s a breakdown into small types of a very big namespace. If we followed your logic that a named type should not be affected, then it should be possible to create a new type with the same name… absurd. 4) that doesn’t work, because I have to maintain 2 sets of enums which is evil as we all know…

Here is a non theoretical example:

enum Security { STOCK BOND }

enum Forex { CASH OPTION }

union Instrument = Security | Forex

In such a class of problems, you simply CANNOT afford name collisions, so not a problem. But you can define subsets without losing the ability to define wholistic ones.

Does that make sense ?

Eric

Le 13 mai 2020 à 17:49, Benjie Gillam notifications@github.com a écrit :

To prefix this comment, please note that I'm one of the people pushing for scalar unions with actual proposed spec edits, so the reason for these questions is so I can fully understand your point of view such that I can consider incorporating it into my proposals.

The moment you encounter those naming collisions is precisely when you no longer need a union.

I don't follow, @ericvergnaud https://github.com/ericvergnaud. Surely removing a union at a later stage in your project would be a breaking change, invalidating many previously valid operations involving the hue field?

This would be part of the spec and should simply be detected as an invalid construct (it is today within an enum)

Having two enums that each contain a value with the same name is perfectly valid according to the current GraphQL spec and reference implementation, so again I don't quite follow what you're getting at here.

Note in my previous comment I express my opinion that defining a "scalar union" elsewhere in the schema should not impact on the values I can add to my original enums - these enums don't "know about" the scalar union, so their evolution should be unaffected by it.

I have this use case where the full set of names can be divided into subsets. I need a named type for each subset, and an umbrella type for the entire set. GraphQL currently will not let me do that, whilst it is a perfectly legitimate ask.

It seems to me that if your enum values are all uniquely named, you could create a "union" enum that just contained all of those values, e.g.

enum Fruit { FRUIT_APPLE FRUIT_BANANA FRUIT_COCONUT }

enum Color { COLOR_RED COLOR_GREEN COLOR_BLUE }

enum FruitColorUnion { FRUIT_APPLE FRUIT_BANANA FRUIT_COCONUT COLOR_RED COLOR_GREEN COLOR_BLUE } Is there a reason this is not a suitable solution to your issue? If so, please could you expand? If I've misunderstood, please could you demonstrate with an example?

To try and make this discussion more concrete, here's an example schema where ORANGE is used in two different enums with two different meanings:

https://codesandbox.io/s/elated-night-i776t https://codesandbox.io/s/elated-night-i776t Could you explain within the context of this schema?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/graphql/graphql-spec/issues/215#issuecomment-627877022, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAZNQJHKLQS3HY7THI6SCMLRRJUJVANCNFSM4CRNVAOQ.

benjie commented 4 years ago

1) what I mean is that if you start seeing naming collisions, it’s about time you revisit your data model. Indeed it will break things, not sure why that’s a problem if it’s required?

One of the greatest things about GraphQL, IMO, is not having to version your schema. The ability to evolve it over time is baked into the GraphQL ethos deeply. Breaking things is a problem, it means your mobile/compiled apps will no longer work, or you have to maintain multiple GraphQL schemas.

3) sorry but I disagree with your principle that : these enums don't "know about" the scalar union, so their evolution should be unaffected by it. A schema is not a bunch of independent names, rather it’s a breakdown into small types of a very big namespace. If we followed your logic that a named type should not be affected, then it should be possible to create a new type with the same name… absurd.

That's a misrepresentation of the principle. Values within the enum are namespaced by the enum. Types within the schema are namespaced by the schema. Fields within a type are namespaced to the type. Saying that because you have a field with a particular name on one type means that you need fields on other types to have different names just because these two types are used in a union would be very very non-ergomatic, the same principle applies to values within an enum. Types implementing an interface "opt in" to the interface explicitly (type ... implements ... in SDL-speak, or new GraphQLObjectType({ interfaces: [...], ...}) in GraphQL reference implementation-speak). To achieve the same kind of opt-in for these enums you might do something like enum Color subsets ColorOrFruit { ORANGE CHERRY }. It'd still require defining the specific items twice, just like a type implementing an interface defines the interface fields again.

4) that doesn’t work, because I have to maintain 2 sets of enums which is evil as we all know…

How is it evil? Surely you can just take the enum values from two different enums and splat them together into a new enum, it's not like you have to define them twice (unless you're doing it SDL-first, in which case use a directive to help you: enum FruitOrColor @valuesFrom(enums: ['Fruit', 'Color']) {}). This seems like a perfectly valid way of solving your specific problem within the constraints of the existing GraphQL spec, and after all one of the guiding principles is "Favor no change".

ericvergnaud commented 4 years ago

Hey,

looks like we are very far from being able to agree on what matters most... No worry, if you’ll accept that different experiences lead to different findings.

imho

1) there’s no way I'm not going to version my schema. Not doing so is making a bet that I got everything right in the first place except for minor future additions. How likely is that? Obviously -and thankfully- adding a field to a type is not breaking (and although I miss wildcard fields in queries I support the principle of not having them for the very reason you mention). So I’m not looking to version every addition. But if I find that: type Team { members: [String] } is not good enough and should evolve into: Team { members: [Employee] } then I will indeed evolve and create a new version, such that consumers of V1 are not impacted by the change. So what I meant is that if you find that the Thing type you use which contains Fruit now also needs to contain Color (thus triggering a naming collision on ORANGE) then it’s probably time to consider a v2 of your schema.

3) my finding is that namespaces are often an excuse for poor naming: "thanks" to namespaces, the same name can be recycled with a different meaning in the same business domain. This is probably one of the biggest source of issues in IT projects. Developers ‘understand’ a name, and make all sort of small decisions based on that understanding, with the underlying assumption that a name can/should only have one meaning, only to find out later that their assumption was wrong. As an example, consider the following: type Team { size: Int } type Company { size: Int }. Since team size probably refers to the number of team members, a lazy developer might assume that company size refers to the number of employees, when it could actually refer to an internal classification by revenue… Developers ARE lazy. So you want to avoid homonyms. (obviously the world is full of homonyms, but if you dig into that you’ll find that it’s not so true within business domains. Having homonyms in a business domain would be catastrophic for the business itself, not just for IT. And surely you don’t want to produce a schema that covers unrelated domains)

I’m not a strong supporter yet of unions of scalars, because I foresee a wide range of problems, notably in statically typed languages (making every value an Object is pushing a lot of work to the consumer). For enums those problems don’t exist, so maybe this subpart of the broader topic could progress at a different speed?

Eric

Le 13 mai 2020 à 21:28, Benjie Gillam notifications@github.com a écrit :

what I mean is that if you start seeing naming collisions, it’s about time you revisit your data model. Indeed it will break things, not sure why that’s a problem if it’s required? One of the greatest things about GraphQL, IMO, is not having to version your schema. The ability to evolve it over time is baked into the GraphQL ethos deeply. Breaking things is a problem, it means your mobile/compiled apps will no longer work, or you have to maintain multiple GraphQL schemas.

sorry but I disagree with your principle that : these enums don't "know about" the scalar union, so their evolution should be unaffected by it. A schema is not a bunch of independent names, rather it’s a breakdown into small types of a very big namespace. If we followed your logic that a named type should not be affected, then it should be possible to create a new type with the same name… absurd. That's a misrepresentation of the principle. Values within the enum are namespaced by the enum. Types within the schema are namespaced by the schema. Fields within a type are namespaced to the type. Saying that because you have a field with a particular name on one type means that you need fields on other types to have different names just because these two types are used in a union would be very very non-ergomatic, the same principle applies to values within an enum. Types implementing an interface "opt in" to the interface explicitly (type ... implements ... in SDL-speak, or new GraphQLObjectType({ interfaces: [...], ...}) in GraphQL reference implementation-speak). To achieve the same kind of opt-in for these enums you might do something like enum Color subsets ColorOrFruit { ORANGE CHERRY }. It's still require defining the specific items twice, just like a type implementing an interface defines the interface fields again.

that doesn’t work, because I have to maintain 2 sets of enums which is evil as we all know… How is it evil? Surely you can just take the enum values from two different enums and splat them together into a new enum, it's not like you have to define them twice (unless you're doing it SDL-first, in which case use a directive to help you: enum FruitOrColor @valuesFrom(enums: ['Fruit', 'Color']) {}). This seems like a perfectly valid way of solving your specific problem within the constraints of the existing GraphQL spec, and after all one of the guiding principles https://github.com/graphql/graphql-spec/blob/master/CONTRIBUTING.md#guiding-principles is "Favor no change".

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/graphql/graphql-spec/issues/215#issuecomment-627985648, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAZNQJCVTQ6MMPSBG6BBGUTRRKN6JANCNFSM4CRNVAOQ.

ericvergnaud commented 4 years ago

Btw there is a reason I believe to be strong enough for treating enums differently.

Here it is:

type Stuff { name: String } can evolve into type Stuff { name: String, size: Int } without breaking clients because they cannot receive size without asking for it...

however when evolving from enum Fruit { ORANGE, BANANA} to enum Fruit { ORANGE, BANANA, APPLE} clients are likely to break when they encounter APPLE which they are not expecting

In short any change in enum names is a breaking change which requires versioning.

From there I believe that union of enums would not create any additional risk.

loganpowell commented 4 years ago

just ran into this limit for the first time, working with a nosql database, which will sometimes kick back an integer (0) for a field and sometimes an object. It would be nice to be able to do this:

type Report {
  total                      : Int
  new                        : Int
  deleted                    : Int
  allocated                  : Allocated
}

union Allocated = Int | Allocation

type Allocation { 
  direct                     : Int
  overlay                    : Int
  signup                     : Int
  upload                     : Int
  other                      : Int
  all_network                : Int
}

I understand that for well-structured data sources, this would be unnecessary

jodinathan commented 3 years ago

I still see no reasonable reason at all to not be able to simply Int | String. Or, using graphql-scalars: Int | String | DateTime for a Cursor union.

benjie commented 3 years ago

With this schema:

union ScalarUnion = Int | String | DateTime

type Query {
  scalarUnionResult: ScalarUnion
}

And this query:

{scalarUnionResult}

Giving this response:

{
  "data": {
    "scalarUnionResult": "2021-01-01T00:00:00Z"
  }
}

Is the value for scalarUnionResult a String or a DateTime? Are you sure? How do you know?

ericvergnaud commented 3 years ago

The response would have to comprise a __typename. (as it already does I believe for non-scalar unions)

jodinathan commented 3 years ago

@benjie because another field validates it to a String or to a DateTime.

benjie commented 3 years ago

@jodinathan Please expand on how you see this being implemented in GraphQL; it's not clear what you mean.

@ericvergnaud You're referring to "boxed types", I think; i.e. {"scalarUnionResult": {"__typename": "Int", "value": 27}}; this has its own complexities but I think with some accomodations could be a viable approach. If you're interested in this subject I suggest you read through the GraphQL Spec Working Group notes and watch the GraphQL Input Unions Working Group recordings on YouTube https://www.youtube.com/channel/UCERcwLeheOXp_u61jEXxHMA and give feedback :+1:

jdehaan commented 3 years ago

Ever since I get notifications on this topic I am irritated about the title of this Proposal. Shouldn't it be better renamed in either

or

I think the latter is worse since one could interpret it then would contain only scalars.

All in all the problem to solve is to find a nice syntax to deliver a value which is not wrapped within a json object {} and without field accessors...

ericvergnaud commented 3 years ago

@ericvergnaud You're referring to "boxed types", I think; i.e. {"scalarUnionResult": {"__typename": "Int", "value": 27}}; this has its own complexities but I think with some accomodations could be a viable approach. If you're interested in this subject I suggest you read through the GraphQL Spec Working Group notes and watch the GraphQL Input Unions Working Group recordings on YouTube https://www.youtube.com/channel/UCERcwLeheOXp_u61jEXxHMA and give feedback 👍

@benjie I am interested but not sure what is the right place to give honest feedback?

benjie commented 3 years ago

@ericvergnaud Depends what it's feedback on; but in general a PR to https://github.com/graphql/graphql-spec/blob/master/rfcs/InputUnion.md factoring your feedback into the RFC document is a good starting point.

dimandzhi commented 3 years ago

I can add some more thoughts to @benjie example.

Is the value for scalarUnionResult a String or a DateTime? Are you sure? How do you know?

I agree, that currently there is no way to differ such scalars in union. My next idea was to at least allow literal unions, which eliminates union of String and DateTime since they are both string literals in result. Result type of Int | String union can be unconditionally determined. My concerns are about Enum type literals and null literal, because in query language they cannot be differed, so I wouldn't allow any unions with Enum. Also, specs doesn't have information about what literals there are and how they differ from each other. "boxed types" is a solution that can be accomplished now without any spec changes. Although, I would love to see at least some scalars allowed in unions.

tsirolnik commented 2 years ago

Consider having a MongoDB instance having two collections - Users and Activities.

The user model has a field named "activities" that holds a reference (Mongoose: mongoose.Schema.Types.ObjectId) to the activities a given user is participating in.

Consider I want to use a union of Activities | String as I want to either load the activities IDs or the activities' documents -

i.e - union ActivityOrActivityID = Activity | String

Currently, this won't work and instead will return -

 Union type ActivityOrActivityID can only include Object types, it cannot include String.

I'm not sure if I'm missing something as I'm not a GraphQL Pro, but it seems like this should be supported as otherwise, to my understanding, one would need to create two different definitions - one for a "flat" user and one for a populated user...

Tiedye commented 2 years ago

This feature would be a big help in migrating schemas that don't use the ID type to schemas that do without introducing breaking changes.

acao commented 2 years ago

@benjie it seems ~1+ year later @oneOf RFC would be the place for this, yes?

benjie commented 2 years ago

I'm not sure what @Tiedye has in mind, but I don't think @oneOf can achieve that.

Tiedye commented 2 years ago

@benjie For migrating argument types: union scalar types would allow it to be done more directly as only one intermediate schema would be required to support the old and new types. My use case is migrating Integer ids to ID ids. For example:

# with union scalar types
type Query {
  post(id: Integer!): Post
}
# to
type Query {
  post(id: Integer! | ID!): Post
}
# to
type Query {
  post(id: ID!): Post
}
# without union types
type Query {
  post(id: Integer!): Post
}
# to
type Query {
  post(id: Integer, new_id: ID): Post # schema no longer encompasses that an id is required
}
# to
type Query {
  post(id: ID, new_id: ID): Post
}
# to
type Query {
  post(id: ID!): Post # front-end has to migrate twice instead of once, very annoying
}
benjie commented 2 years ago

With @oneOf:

type Query {
  post(by: PostSpec!): Post
}
input PostSpec @oneOf {
  pk: Integer
}
# to
input PostSpec @oneOf {
  pk: Integer
  id: ID
}
# to
input PostSpec @oneOf {
  id: ID
}

I don't think there's any way to support union scalar types in general (without boxing like @oneOf does) because GraphQL cannot tell the difference between a String, an ID and a custom scalar that's encoded as a string.

Tiedye commented 2 years ago

Why would it need to tell the difference? Argument types are specified in the query, and there is no overloading in GraphQL

benjie commented 2 years ago

Keep in mind custom scalars can do basically anything. Say for example you have:

scalar Base64EncodedJSON

When you send this to the server, you'll send something like: "eyJoZWxsbyI6IndvcmxkIn0="

When the server receives it, it would then "parse" it into an internal representation, which may well be an object such as (JSON syntax): {"hello":"world"}.

From a client perspective, Base64EncodedJSON and ID and String all look the same - they're strings.

From a schema point of view, there's nothing that tells GraphQL that the argument to myField(with: Base64EncodedJSON! | String!): ReturnType is not valid. The schema author might even expect it to work - give me a string, or give me the JSON. However, when we come to [CoerceVariableValues()](https://spec.graphql.org/draft/#CoerceVariableValues()) or similar, GraphQL does not know what parse method to call - is it dealing with a string, or base64-encoded JSON? How can it know?

Tiedye commented 2 years ago

I think it is acceptable to run all applicable parse methods and if multiple succeed either: throw an error, or preferably have some tiebreak like first type in the union. There is likely a more refined way of doing the tiebreaks, but I think it is far from not doable. The extra execution cost is minimal and worth it for the use case I see myself using it for.

rivantsov commented 2 years ago

being on the server side, I agree with @benjie, more troubles (recognizing what the heck is this token) than benefits. A simple oneOf wrapper type would provide equivalent functionality without ambiquities

Tiedye commented 2 years ago

oneOf just doesn't solve the problem sadly :(

aitorllj93 commented 2 years ago

Another approach inspired by @rmosolgo’s recommendation would to have a type system that looks like:

union IntOrString = IntBox | StringBox

type IntBox {
  value: Int
}

type StringBox {
  value: String
}

With a query that looks like:

fragment intOrString on IntOrString {
  __typename
  ... on IntBox { intValue: value }
  ... on StringBox { stringValue: value }
}

This sounds like ValueObjects to me so I think it's the best approach right now:

union ScalarValue = IntValue | StringValue;

type IntValue{
  value: Int
}

type StringValue{
  value: String
}
fragment scalarValue on ScalarValue {
  __typename

  ... on IntValue { 
     value 
  }
  ... on StringValue { 
    value 
  }
}
ThisIsRuddy commented 2 years ago

I too would enjoy the benefits of using enums in unions, it would allow me to modularise my directive inputs options which I currently have to use one big enum for 😅

psirenny commented 1 year ago

The lack of a union scalar type exacerbates the null/undefined problem and API authors/consumers largely avoid boxing because of how unergonomic it is. A None enum combined with scalars would allow to disambiguate between the many meanings of null.

ggepenyan commented 1 year ago

Let's say I have this:

export const UserUnion = createUnionType({
  name: 'OpponentUnion',
  types: () => [Number, User],
});

where User is ObjectType. and later I do:

@Field(() => UserUnion)
defender: number | User;

Which I'm not able to do because of an error. Even if I use a custom Scalar for the Number.

ggepenyan commented 1 year ago

I'm able to do this without an error: @Field((type) => [Int, User]) But later in the schema.gql I get this: defender: [Int!]! Which is not what expected.

benjie commented 1 year ago

@ggepenyan What you are writing seems specific to whatever GraphQL framework you are using, please file issues there instead. The GraphQL spec only allows object types to be members of unions at this time.

CRC-Mismatch commented 1 year ago

Please correct me if I'm wrong at any rate, but I believe a use-case example would be the GraphQL spec's own "error type" specification in itself... According to the examples in https://spec.graphql.org/draft/#sec-Errors, the error's path element would be a list of both Strings and Ints. As it currently stands, a GraphQL API would be unable to reproduce its own GraphQL errors in a valid schema as-is (for example if I want to reproduce such errors in a log query of sorts).

biggestredapple commented 10 months ago

Hi @benjie I also encountered these two issues @ggepenyan mentioned above. but no way to resolve this issue. I used nestjs/graphql. Please let me know the solution. Thanks

benjie commented 10 months ago

@biggestredapple Please see my reply to that comment.

biggestredapple commented 10 months ago

Hi @benjie. Please take a look at the code below and let me know the solution.

import { Field, InputType } from '@nestjs/graphql';
import { IsString, ValidateNested } from 'class-validator';

@InputType()
export class BaseDto {
  @Field()
  @IsString()
  name: string;
}

@InputType()
export class Item1Dto extends BaseDto {
  @Field()
  @IsString()
  type: string;

  @Field(() => [Item1Dto , Item2Dto])
  @ValidateNested({ each: true })
  items: (Item1Dto | Item2Dto)[];
}

@InputType()
export class Item2Dtoextends BaseDto {
  @Field()
  @IsString()
  type: string;
}

@InputType()
export class CreateItemBaseDto extends BaseDto {
  @Field(() => [Item1Dto , Item2Dto], { nullable: false })
  items: (Item1Dto | Item2Dto)[];
}
benjie commented 10 months ago

Hi @biggestredapple, what you are writing seems specific to the GraphQL framework you are using (@nestjs/graphql), please file issues there (https://github.com/nestjs/graphql) instead.

biggestredapple commented 10 months ago

okay, Thanks