# Allow @hasInverse on union types

**URL:** <https://discuss.dgraph.io/t/allow-hasinverse-on-union-types/11618>\
**Category:** GraphQL\
**Tags:** kind:question, kind:enhancement\
**Created:** [November 25, 2020, 8:57am UTC](https://discuss.dgraph.io/t/allow-hasinverse-on-union-types/11618 "2020-11-25T08:57:27Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![maaft](https://avatars.discourse-cdn.com/v4/letter/m/4af34b/32.png) [@maaft](https://discuss.dgraph.io/u/maaft)\
**Post date:** [November 25, 2020, 8:57am UTC](https://discuss.dgraph.io/t/allow-hasinverse-on-union-types/11618/1 "2020-11-25T08:57:27Z")

</div>

Why are we not allowed to use inverse relationships on union types? E.g.

```auto
type Foo {
   m: [Bar!]! @hasInverse(field: foo)
}

union Bar = Blub | Blabla

type Blub {
   foo: Foo!
}

type Blabla {
   foo: Foo!
}

```

throws:  
`Type Foo; Field m: Field m is of type Bar, but @hasInverse directive only applies to fields with object types`

This makes adding childs to Foo impossible (unless you create them together with the parent)

Or am I doing something wrong?

I understand that I can use interfaces instead, this however leads to issues during code-generation for my apollo client as the interface type will be translated to a “normal” type where you can create instances of (which shouldn’t be allowed) (see: [Can I generate correct union types for interface fields? · Discussion #5125 · dotansimha/graphql-code-generator · GitHub](https://github.com/dotansimha/graphql-code-generator/discussions/5125))

---

<div class="post-metadata">

**Author:** ![abhimanyusinghgaur](https://yyz1.discourse-cdn.com/flex007/user_avatar/discuss.dgraph.io/abhimanyusinghgaur/32/2980_2.png) [@abhimanyusinghgaur](https://discuss.dgraph.io/u/abhimanyusinghgaur)\
**Post date:** [November 25, 2020, 10:06am UTC](https://discuss.dgraph.io/t/allow-hasinverse-on-union-types/11618/2 "2020-11-25T10:06:31Z")

</div>

As per [GraphQL spec](http://spec.graphql.org/June2018/#sec-Unions):

> GraphQL Unions represent an object that could be one of a list of GraphQL Object types, but provides for no guaranteed fields between those types.

Means, one can’t assume anything about the fields in member types of a union. If there are common fields in two types, then you should be using interfaces in your schema design to represent that abstraction.

Just for counter-example, I can add one more type to the union `Bar` like this, which doesn’t have a field `foo`:

```auto
type Foo {
   m: [Bar!]!
}

union Bar = Blub | Blabla | MyType

type MyType {
   myField: String
}

type Blub {
   foo: Foo!
}

type Blabla {
   foo: Foo!
}

```

The schema is still valid GraphQL, but now you can’t really apply `@hasInverse` because there is no field named `foo` in type `MyType`.

So, `@hasInverse` doesn’t make semantic sense with unions.

---

<div class="post-metadata">

**Author:** ![maaft](https://avatars.discourse-cdn.com/v4/letter/m/4af34b/32.png) [@maaft](https://discuss.dgraph.io/u/maaft)\
**Post date:** [November 25, 2020, 10:36am UTC](https://discuss.dgraph.io/t/allow-hasinverse-on-union-types/11618/3 "2020-11-25T10:36:22Z")

</div>

But `@hasInverse` has no relevance outside of dgraph anyway, so does it really matter if the schema would still be valid? Dgraph could just check if all union members implement the required field and throw an error if not.

Alternatively, we could be more specific and allow `@hasInverse` only on unions which members implement the same interface:

```auto
type Foo {
   m: [Bar!]! @hasInverse(field: foo, interface: Hello)
}

interface Hello {
   foo: Foo!
}

union Bar = Blub | Blabla

type Blub implements Hello {
   ...
}

type Blabla implements Hello {
   ...
}

```

---

<div class="post-metadata">

**Author:** ![abhimanyusinghgaur](https://yyz1.discourse-cdn.com/flex007/user_avatar/discuss.dgraph.io/abhimanyusinghgaur/32/2980_2.png) [@abhimanyusinghgaur](https://discuss.dgraph.io/u/abhimanyusinghgaur)\
**Post date:** [November 25, 2020, 11:12am UTC](https://discuss.dgraph.io/t/allow-hasinverse-on-union-types/11618/4 "2020-11-25T11:12:57Z")

</div>

> [@maaft](#):
>
> does it really matter if the schema would still be valid?

It is not about the validity of the schema, but promoting a better schema design. Dgraph could surely check the one-off special case of all the member types in a union having a common field. But, that is the exact reason why GrpahQL has interfaces. That is what interfaces are supposed to do. That is what it has always been with GraphQL: a better schema design to begin with. We have had discussions about this particular issue internally previously as well, and we decided not to allow `hasInverse` with unions because of the same reasons.

The intention of Unions is to be able to represent _particular instances_ of different types, not all the instances of them in general. @michaelcompton has explained this tiny bit of difference here brilliantly:

> [@Union types in GraphQL](http://discuss.hypermode.com/t/union-types-in-graphql/9581/5):
>
> This brings me to another thought - if there is no `addHomeMember` mutation, how can I query for HomeMembers ? It looks like in the way it’s presented that the union types only make sense as the types of fields … which means that the only way access them should be by following an edge inside a type to a union. So the query type on it’s own doesn’t make sense to me - seems to only make sense as a filter on the fields inside a type.
> 
> Also doesn’t make semantic sense here - for example in your schema everything that’s a dog, or parrot becomes an instance of type HomeMember, but I don’t think that’s the intention of unions. I think the intention is that a particular parrot or dog might be the member of a particular home because it’s linked through the `members` field of a particular home.

You should be using an interface in this case, like this:

```auto
type Foo {
   m: [Hello!]! @hasInverse(field: foo)
}

interface Hello {
   id: ID!
   foo: Foo!
}

type Blub implements Hello {
   ...
}

type Blabla implements Hello {
   ...
}

```

and not a union.

> [@maaft](#):
>
> This makes adding childs to Foo impossible (unless you create them together with the parent)

For this particular case, you can always update `Foo` to add more children.

I believe the code generator that you are using, although it uses a `type` to represent `interface` in typescript, it is up to you to use it. You can choose to not create its instances, and anyways, if the GraphQL server you are interacting with doesn’t allow creating interfaces, you will eventually get an error from there if you do a mutation to create an interface. I understand that it feels weird creating instances of an interface on the client-side, but if the code generator is using `type` to represent `interface`, then either there is a reason behind it or that is something that can be improved there.

[EDIT]: We could surely re-discuss it again, and see if we should allow `hasInverse` on Unions, but it doesn’t make a semantic sense, is what has been the reason behind not allowing it yet after all.

---

<div class="post-metadata">

**Author:** ![maaft](https://avatars.discourse-cdn.com/v4/letter/m/4af34b/32.png) [@maaft](https://discuss.dgraph.io/u/maaft)\
**Post date:** [November 25, 2020, 11:28am UTC](https://discuss.dgraph.io/t/allow-hasinverse-on-union-types/11618/5 "2020-11-25T11:28:39Z")

</div>

Thank you for your time and explanation. I also think that the codegen must be responsible to generate the correct types but wanted to ask if this problem was discussed here before and you perfectly answered this.

Just for further explanation as I felt my issue wasn’t clear (don’t bother to read, it’s completely unrelated to dgraph and more an issue with the codegen I’m using):

The codegen I’m using takes this schema:

```auto
type Foo {
   m: [Bar!]! @hasInverse(field: foo)
}

interface Bar {
   foo: Foo!
}

type Blub implements Bar {
   ...
}

type Blabla implements Bar {
   ...
}

```

generates theses types:

```auto
export type Bar = {
  foo: Foo;
};

export type Blabla = Bar & {
  __typename?: 'Blaba';
  foo: Foo;
  ...
};

export type Blub = Bar & {
  __typename?: 'Blaba';
  foo: Foo;
  ...
};

export type Foo = {
  __typename?: 'Foo';
  m: Array<Bar>;
};

```

As you can see, filed `m` of `Foo` is of type `Bar`. So I have no way of differentiating between the actual types (or read their fields) when using these generated types. Which defeats the purpose of code generation in this case.

This is of course completely unrelated to dgraph and I opened an issue on the codegen repo, I just had the feeling to explain myself.
