hey, working on a custom client directive i want t...
# apollo-kotlin
a
hey, working on a custom client directive i want to adjust codegen + operations output based on the applied directive. I plan on using a compiler plugin w/
registerForeignSchemas
to define a custom directive. Is this the right approach? or do i also need to define it in my repos
extra.graphqls
file?
i guess - is there a way for compiler plugins to contribute custom client-only directives?
or is
registerSchemaCodeGenerator
the better approach? i want ide autocomplete on the directive too
m
Do you have an example?
The ability to tweak the Kotlin output is very limited. Basically the only possibility is to change the
KotlinPoet
specs
a
sure. so
Copy code
query MyQuery @split(dataFieldNames: "foo") {
     title
     subtitle
   foo {
      id
      dataBar
    }
}
then outputs two queries: 1. transform original query to include a variable called
includeData
,
Copy code
query MyQuery($includeData: Boolean! = true) {
  someUIComponent {
      title
     subtitle
   foo @include(if: $includeData) {
      id
      dataBar
    }
   }
}
this enables clients to call query with all data
foo
or just configuration data which we can cache separately 2. output a data only query (TBD on proper format yet):
Copy code
query MyQueryData {
   foo {
     id
     dataBar
   }
}
m
For this you can use
registerExecutableDocumentTransform()
It'll take a
GQLDocument
with all your queries and fragments as input and you can return a new document that, for each query generates 2 version of them
You'll have a bunch of AST manipulation to do but sounds like it should work
a
yeah i got that part mostly working. i was wondering about client directives though. I want the compiler plugin to contribute them automatically, so that the consuming repo does not need to redeclare them
m
Ah sorry I missed that. Since the client directive is processed by your transform, and the transform runs before validation it shouldn't be an issue
Unless you're talking about IDE support?
a
yeah IDE support for the providing directive. so far ive redeclared it in
extra.graphqls
.
ok now that ive got it generating things. when I have a reusable fragment, I am right now ducplicating them with the
@include/@skip
with the variable in the fragment body (since fragment arguments are not officially supported yet in graphql) . but since they are identical, im looking to try to remap them to reuse the same generated types (since the data field from another subgraph is nullable enforced at router level)
(to also avoid bloating app with duplicates, which also makes parsing much more cumbersome and duplicative). I could try to make them output in common interface, but id rather just reuse existing concrete types
for example take:
Copy code
fragment FooFragment {
  foo {
    id
    dataBar
  }
}
vs:
Copy code
fragment FooFragmentSplit {
  foo @include(if: $includeData) {
    id
    dataBar
  }
}
is there a way to then intercept code gen for the
FooFragmentSplit
to just reuse
FooFragment
?
maybe its IR?
m
IDE support for the providing directive
I'm not sure if the IDE plugin can read the directives provided by the compiler plugins? Summon @bod
is there a way to then intercept code gen for the
FooFragmentSplit
to just reuse
FooFragment
?
I think the same
ExecutableDocumentTransform
should work?
b
IDE support: I think there's a feature that tries to do this but doesn't work 😅 I'll dig a bit to refresh my memory.
m
Do you want this in the end?
Copy code
fragment FooFragment on Foo {
  foo {
    id
    dataBar
  }
}
fragment FooFragmentSplit on Foo {
  ...FooFragment @include(if: $includeData) 
}
b
ah yes I remember now: a JAR in your project can provide graphql files - but that only works with simple (single module) projects.
a
ah i see. and @mbonnin no, i want it to have separate graphql fragment selections so the include is inlined into the fragment that needs it, but then reuse the generated kotlin code for both, since the shape are identical. that way i dont end up with copies in my large app with many graphql modules and queries (100s of queries) . I want to ensure that: fragment reuse can still happen in places without the directive on the top level query, but then also rewrite them to be different on the server, but then client code just reuses them together (as if the type was no different)
m
Yea, we don't have anything like this
a
in language like typescript this would be easy and straightforward due to duck typing
m
It would be a global optimization I guess. Check the shape of all models and dedup them
There has been talks about it. In the general case, it's hard because of naming. How should you name the deduplicated model
In your case there is a more natural name possible
But it's also a particular case of a more general possible optimisation. If we do this, we should probably do this everywhere
a
right. it gets tricky in multimodule environment too, since the classes themselves need to be available in the calling modules as resolvable types, so a global solution wouldnt quite work here. I think there is a way if we had access to fragment -> generated class mapping hook to tell it to either reuse existing type by mapping it to this other type that is generated already, or provision a new type
m
Yeaa, it's not easy
Probably you can do it in IR if you really want to but that's not going to be straightforward
a
yeah it probably wont work, which is fine. also on ios (swift) there is no compiler plugin support anyways
m
in language like typescript this would be easy and straightforward due to duck typing
Yea, for this reason GraphQL is a lot easier in TS
I asked at some point whether there were plans to introduce something like this in Kotlin, with autocomplete in the IDE but erased types at runtime and the answer wasn't a "no"
Like something could be done with contracts maybe
But I wouldn't hold my breath
a
true, also just by enabling TypeAlias it could in theory work, if the generated code was using typealias. ill try that too
m
Let me know how that goes!
a
Thank you! I believe i got compilation working with fake objects that have properties that resemble the code gen output for adapters that are assigned to companions of the “regular” fragment types. So it magically compiles so far. When I’m back at computer I can share what I did 😅
🙌 1