prismake / typegql

Create GraphQL schema with TypeScript classes.
https://prismake.github.io/typegql/
MIT License
423 stars 21 forks source link

complex object types which are both output and input #33

Open capaj opened 6 years ago

capaj commented 6 years ago

currently I have this in my app:

@ObjectType()
export class OrganisationFeatures extends ConstructorAssigner {
  @Field() campaigns: boolean
  @Field() custom_email_sender_address: boolean
  @Field() OKTA_SAML: boolean
  @Field() search_analytics: boolean
  @Field() SCORM: boolean
  @Field() custom_font: boolean
}

@InputObjectType()
export class OrganisationFeaturesInput extends ConstructorAssigner {
  @InputField() campaigns: boolean
  @InputField() custom_email_sender_address: boolean
  @InputField() OKTA_SAML: boolean
  @InputField() search_analytics: boolean
  @InputField() SCORM: boolean
  @InputField() custom_font: boolean
}

I'd love to be able to define both at once doing:

@DuplexObjectType()  // using a special type I can register both at once
export class OrganisationFeatures extends ConstructorAssigner {
  @Field() campaigns: boolean
  @Field() custom_email_sender_address: boolean
  @Field() OKTA_SAML: boolean
  @Field() search_analytics: boolean
  @Field() SCORM: boolean
  @Field() custom_font: boolean
}
pie6k commented 6 years ago

I was really willing to implement it from start of this project, but spec of graphql says:

http://facebook.github.io/graphql/October2016/#sec-Input-Objects

The Object type defined above is inappropriate for re‐use here, because Objects can contain fields that express circular references or references to interfaces and unions, neither of which is appropriate for use as an input argument. For this reason, input objects have a separate type in the system.

That was ther reason they're separated.

You can try tools like graphql-compose eg:

import { TypeComposer } from 'graphql-compose';

const yourConvertedInputType = TypeComposer.create(YourOutputType).getInputType();
capaj commented 6 years ago

@pie6k that's doable, but I'd have to compile my class into a GQL schema. Which is another extra line :confused:

pie6k commented 6 years ago

Thanks for your PR.

I think that maybe another type may not be even needed. What about auto-converting it when @ObjectType is used as input type on the fly?

So we could do

@ObjectType()
export class FooType {
  @Field() bar: string;
}

and ten simply use it as argument type

@SchemaRoot()
export class FooType {
  @Mutation foo(input: FooType) bar: string;
}

That would feel more intuitive than having another type.

It would be good to check if given ObjectType is inputable eg. it does not have any recurrsive fields etc.

I'll think about it as I don't want to make typegql 'frameworkish`.

If that could be accomplished with reasonable api, then actually I stop seeing point of even having InputType at all. We could just validate types of arguments for if they're able to be input.

capaj commented 6 years ago

@pie6k thanks! Your solution sounds much better than having an extra type.

By recursive fields you mean what exactly? Even if Footype has a complex type in a field, it all works fine as far as I've tried it.

pie6k commented 6 years ago

Recursive field = User has Dog > Dog has owner User > owner User has Dog > Dog has owner user etc... Recursive = Circular and it's not possible to reflect it in input field.