hasura / graphql-engine

Blazing fast, instant realtime GraphQL APIs on your DB with fine grained access control, also trigger webhooks on database events.
https://hasura.io
Apache License 2.0
30.94k stars 2.74k forks source link

Feature Request: Support graphql union types #2311

Open heyrict opened 5 years ago

heyrict commented 5 years ago

Currently the only method to do query multiple objects in the same time is to define an custom sql function:

CREATE OR REPLACE FUNCTION search(text TEXT)
RETURNS SETOF search_trigger LANGUAGE sql STABLE AS $$
SELECT id, name,height, NULL AS primary_function, NULL AS length, 'human' AS type FROM humans
WHERE (...)
UNION ALL
SELECT id, name, NULL AS height, primary_function, NULL AS length, 'droid' AS type FROM droids
WHERE (...)
UNION ALL
SELECT id, name, NULL AS height, NULL AS primary_function, length,  'starship' AS type FROM starships
WHERE (...)
$$

while in the front end we query the search_trigger

query {
  search(text: "an") {
    __typename
    id
    name
    height
    primary_function
    length
    type
  }
}

While this should work, we have another type search_trigger instead of article and user in the front end, which can cause many inconveniences, e.g. write new interface and types, inconsistency in local store, etc. It will be good to have support for union type so that we can query with:

query {
  search(text: "an") {
    __typename
    ... on Human {
      name
      height
    }
    ... on Droid {
      name
      primaryFunction
    }
    ... on Starship {
      name
      length
    }
  }
}

Database schema in the examples is borrowed from https://graphql.org/learn/schema/#union-types.

jorroll commented 5 years ago

Related #2323

isidentical commented 3 years ago

Unions are a fundamental way of representing ADTs (algebraic data types). It would be awesome to have them hasura

hden commented 3 years ago

I wonder how would you deal with mutations?

mac2000 commented 3 years ago

The same is true for adding custom actions - without the ability to use union it is not possible for an endpoint to return different types at once, and what even worse because of type checking and everything else there is no way to work around it like in the example above

Svarto commented 3 years ago

+1, Ran into this limitation as well, would love union type support to be added to Hasura.

pierre-lucas40 commented 3 years ago

Same here. I need to implement inheritance and queries which would return a union type.

maxcan commented 3 years ago

+1 would love this

field123 commented 3 years ago

+1 It would be a feature that few other competing frameworks support.

@coco98 is there a way to vote up this feature or get it on the roadmap?

maxcan commented 3 years ago

I believe that using table inheritance and a foreign key pointing at the ancestor table, this could be modeled pretty consistently for Postgres. I worry that with multi-db support, it may be impossible to make this work across all platforms.

field123 commented 3 years ago

@maxcan is there a workaround to support unions with the current version of hasura, through remote schemas or some other combination of features?

maxcan commented 3 years ago

@field123 I was thinking that you might be able to use table inheritance and have a single foreign key point at the parent table but the branches of the union would be child tables and a separate manual reference set up for each.

However, it seems that you can't use table inheritance for that since a foreign key can only reference the parent table, not a union of all the rows of the children of the parent.

Probably the only way to do this is to have a child object for each branch of the union but then use a check constraint to ensure that all but one of the child object IDs are null. You won't get a union type but you'll get a set of nullable fields and assurance that at most one of them is populated.

studious commented 2 years ago

I was just beginning to lean into Union types heavily in our Rails portion of the graph only to find out Hasura is lacking this support given how coupled it is to the database as source of truth. Union types and Interfaces have started becoming rather helpful. I'd love to see something announced as our plan is to move even more to Hasura.

lemes commented 2 years ago

I thought of a possible workaround which I'd appreciate the feedback on.

  1. Create a view as follow
    
    create view search
    select 
    "Human" as __hasura_union_typename, # todo: avoid hard coded value, use the table name instead (??)
    h.id as human_id,
    null as droid_id,
    null as starship_id,
    h.name as name
    from human

union all

select "Droid" as __hasura_union_typename, null as human_id, d.id as droid_id, null as starship_id, d.name as name from droid d

union all

select "Startship" as __hasura_union_typename, null as human_id, null as droid_id, s.id as starship_id, s.name as name from starship s


2. Create an object relation for `human_id`, `dropd_id` and `starship_id`.

3. Query
```graphql
query {
  search {
    __typename
    __typename_hasura_union
    name # same field between the interfaces would work too
    human {
      name
      height
    }
    droid {
      name
      primaryFunction
    }
    starship {
      name
      length
    }
  }
}
  1. (Optional) If you REALLY want the same union behaviour as per GraphQL specs, you technically could (I think) add some custom transformers in the GraphQL client of your preference to change the results.

  2. (Optional) Not satisfied? How about a custom transformer for the GraphQL AST query that turns the query a GraphQL union spec query into the example above?

Item 4 and 5 could be open-sourced plugins for different GraphQL client libraries and parsers. Hasura could even extend the GraphQL spec like Relay does.