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
31.14k stars 2.77k forks source link

console and CLI: throw intuitive error messages when unsupported defintions are used in custom types #4666

Open frossi85 opened 4 years ago

frossi85 commented 4 years ago

Hi,

I am trying to create an action to call one of my rest endpoints but it seems that Hasura does not support fully GraphQL specs. Here is the action definition:

type Query {
  searchAssets (search: String): [Asset]
}

#4579 Types definitions

type Asset {
  friendlyName: String!
  description: String!
  symbol: String!
  type: AssetType!
  logo: String
  metadata: AssetMetadata
}

enum AssetType {
  Stock
  Future
  Bond
  Securities
  InternationalStock
  Check
  Option
  TreasuryBills
  FixedIncomeSecurities
  Mocked
}

union AssetMetadata = CompanyMetadata | OtherMetadata

type CompanyMetadata {
  ceo: String!
  headQuarters: String!
  foundedAt: Int!
  employees: Int!
}

type OtherMetadata {
  other: String!
}

When I click on the Create button basically nothing is happening, there is not error or any feedback. So my guess was that probably union was not supported, even if it was part of GraphQL spec. So when I removed all the metadata related types and field the action was created. Then I tried to create just one metadata type with all optional types like this:

type Query {
  searchAssets (search: String): [Asset]
}

Types definition

type Asset {
  friendlyName: String!
  description: String!
  symbol: String!
  type: AssetType!
  logo: String
  metadata: Metadata
}

enum AssetType {
  Stock
  Future
  Bond
  Securities
  InternationalStock
  Check
  Option
  TreasuryBills
  FixedIncomeSecurities
  Mocked
}

type Metadata {
  ceo: String
  headQuarters: String
  foundedAt: Int
  employees: Int
  other: String
}

I got this error:

validation for the given custom types failed because the type "Metadata" of the field "metadata" in the object type "Asset" is object type which isn't allowed

Which is strange because GraphQl also supports it, and seems a really important feature to be able to a couple already implemented APIs with Hasura Action. The first issue while I would love to see it is not a real stopper but the seconds one it is.

So the question if these are bug or current limitations, and when you are planning to implement them if that is the case.

Thanks

wawhal commented 4 years ago

Hey @frossi85 You ran into some limitations of custom types with Hasura.

  1. It is currently not possible to define Interfaces and Union types as custom types
  2. Hasura does not allow a field of an object type to be another object type, i.e. the fields of an object type can only be scalars and enums. For a more complicated structure, the object types can be connected to the rest of the graph via relationships.

Read more about custom types here.

However, not getting a valid feedback on an error is a bug, so I am rescoping this issue to return proper feedback when an error happens.

frossi85 commented 4 years ago

Thanks for the quick response, I did not get this:

For a more complicated structure, the object types can be connected to the rest of the graph via relationships.

I read the docs but not sure how that can be achieved with my current API. Are you suggesting that I split my API in 2, one for the root object, and other for the metadata object and then relate them in some way?

Also, the Hasura team has plans to implement Interfaces and Union types as custom types? If it is a yes, do you have an estimate like 2020 Q4 or some approx?

frossi85 commented 4 years ago

Also in cases like this, I think that having a way to define some mapping function in the actions to map API responses to the output type, plus cache over those API calls and relationships between Actions not only with a table will solve this issue without implementing that objects can contain objects.

How? In my example, I would create two actions calling the same API URL, in one I would define the type:

type Metadata {
  ceo: String
  headQuarters: String
  foundedAt: Int
  employees: Int
  other: String
}

with a mapping function like this: response.map(x => x.metadata)

Then define another action with this type:

type Asset {
  friendlyName: String!
  description: String!
  symbol: String!
  type: AssetType!
  logo: String
}

And then define a relationship from Asset to Metadata.

So if the cache is in place my API will be called just one time and I will be able to construct more advanced graphql schemas without changing my old API.

The idea to create a graphql server to overcome the limitations of Hasura goes against the philosophy of Hasura (at least si what I see that you are trying to do) of developing Graphql API as fast as u can without friction and with the ability of reuse old APIs that you already built.

I know you are doing the best guys and this is great but this is just an idea on how maybe you can enhance the product because mapping function will not help just in this scenario but in many other, like my old API use this casing in the response: { my_prop } but with Hasura we are trying to use this: { myProp } or the other way around.

I hope the feedback helps you to keep rocking it.

FickleLife commented 4 years ago

FYI, I have stumbled across this issue trying to use Actions to deliver results to Dart types (Flutter), using Artemis to code gen all the types based on the gql schema and queries. I'll have to rethink how to strongly type nested Actions outputs.

The somewhat cryptic error I get is the type "HourlyResult" of the field "hourlyResult" in the object type "Hourly" is object type which isn't allowed

abdullah2993 commented 3 years ago

any roadmap for implementing the feature?

kolharsam commented 3 years ago

@abdullah2993 as you can see, work had started in #5434 and it's been stalled at the moment. The changes on the console are done, and similar changes on the CLI were required. We will pick this up again, soon.

cc @beerose

jifeon commented 3 years ago

@frossi85

And then define a relationship from Asset to Metadata.

According to docs you can define only relation between action and table, but not between 2 actions. Probably I missed something?

AlexKenbo commented 3 years ago

We need to make a response for an action.

And the response must contain a list of objects. But we get an error that the type of the field cannot be an object.

Hasura v 1.3.3

Снимок экрана 2021-05-28 в 11 50 37
AlexKenbo commented 3 years ago

We found such a solution - use jsonb

type Response {
   items: jsonb!
}
jumpdol commented 3 years ago

Also in cases like this, I think that having a way to define some mapping function in the actions to map API responses to the output type, plus cache over those API calls and relationships between Actions not only with a table will solve this issue without implementing that objects can contain objects.

How? In my example, I would create two actions calling the same API URL, in one I would define the type:

type Metadata {
  ceo: String
  headQuarters: String
  foundedAt: Int
  employees: Int
  other: String
}

with a mapping function like this: response.map(x => x.metadata)

Then define another action with this type:

type Asset {
  friendlyName: String!
  description: String!
  symbol: String!
  type: AssetType!
  logo: String
}

And then define a relationship from Asset to Metadata.

So if the cache is in place my API will be called just one time and I will be able to construct more advanced graphql schemas without changing my old API.

The idea to create a graphql server to overcome the limitations of Hasura goes against the philosophy of Hasura (at least si what I see that you are trying to do) of developing Graphql API as fast as u can without friction and with the ability of reuse old APIs that you already built.

I know you are doing the best guys and this is great but this is just an idea on how maybe you can enhance the product because mapping function will not help just in this scenario but in many other, like my old API use this casing in the response: { my_prop } but with Hasura we are trying to use this: { myProp } or the other way around.

I hope the feedback helps you to keep rocking it.

did u fix that issue?

rossano-gallardo commented 3 years ago

Gents, I have a service that will return a complex object as output, I need all those arrays of objects and other properties as objects to serve my front end. Without this feature I will have to make my front end do a call to this service to then call hasura with the returned data. actions without been able to do arrays of object types and object types as properties seem only to work when you don't need much data from other datasources

aindong commented 3 years ago

We found such a solution - use jsonb

type Response {
   items: jsonb!
}

Having the same problem but yeah, using just jsonb solves it