gql-dart / ferry

Stream-based strongly typed GraphQL client for Dart
https://ferrygraphql.com/
MIT License
593 stars 113 forks source link

[question]: Freezed support #578

Closed Abhilash-Chandran closed 5 months ago

Abhilash-Chandran commented 5 months ago

Hi Team,

I was trying out ferry for our first-ever graphql client project. Is there a reason why there is no freezed based serialization made available in ferry_generator? I understand this is open source and I can also contribute. I have experience creating a fork of existing open-api generator which uses freezed as its model/serialization library. Before I try it I would like to know if there is a specific reason we chose built_value and not freezed.

knaeckeKami commented 5 months ago

I am not the original author of ferry but took over maintenance, but I am one of the first users of ferry. But I can give my thoughts on the possible rationale of the original author:

query HeroQuery {
   hero { 
     ...HeroFragment
     height
     weight
 }
}

fragment HeroFragment on Hero {
   id
   name
}

This will generate a HeroFragment abstract class, and a HeroQuery_hero class that implements the HeroFragment class, so you can write code that accepts fragment types which are shared across many queries.

Similarly, when using inline fragments with type conditions, like

fragment DroidFragment on Droid {
  primaryFunction
}

query HeroForEpisode($ep: Episode!) {
  hero(episode: $ep) {
    name
    friends {
      name
    }
    ... on Droid {
      ...DroidFragment
    }
  }
}

We generate a base class GHeroForEpisodeReq_heroBase, which has the common fields, and specialized classes for the type conditions, in this case GHeroForEpisodeReq_heroOnDroid, which has the common fields plus the fields that are specific to this typecondition.

These are the two features of ferry_gererator that go a bit beyond basic data classes.

The first versions of ferry were published in 2019. built_value was already out and stable at that point and supported all of these features.

It supported union types (even though in a bit crude way) for fragment spreads with type conditions and subtypes (for inline fragment spreads).

I don't know if freezed even existed at that point, but if it did, it was probably a very new and unstable package and did not yet have support for this kind of subtyping.

I think built_value was the reasonable choice in 2019. Nowadays, with Dart having native support for sealed classes, null safety (yes, built_value supported null safety using runtime checks before Dart had null safety), etc. it might look differently.

Of course, the syntax of built_value is cumbersome, and also the single serializers.dart file which lists all the serializers for all the generated types prooves to be problematic for bigger projects.

Another thing is that since early 2021, the Dart team announced that it explored static metaprogramming.

While still unclear on if this will ever be released and how exactly it would look like, for people working on code generators that brings uncertainty, because of the risk of spending many hours on a project that could be outdated soon because of language level support for macros.

ferry does not directly depend on ferry_generator, as long as any generator uses the OperationRequest interface, it will work fine, so in principle, one could implement another code generator that uses freezed or implements serialization manually (I also like what graphql_codegen does for example).

Abhilash-Chandran commented 5 months ago

@knaeckeKami Thanks for the detailed answer. Now that static metaprogramming is already out in dart master branch, i think the development is going good on its way. So i dont see any reason why we shouldn't approach freeazed serializers as well. I will walk through the code and see how much effort it would require. I am not sure if ferry_generator is confirgurable by design. If not, then this would need a separate package say ferry_freezed_generator.

knaeckeKami commented 5 months ago

most of the actual codegen logic is in gql_code_builder, ferry_generator is mostly just a wrapper that uses gql_code_builder to generate the OperationRequest classes that ferry expects (gql_code_builder is also used by other graphql code builders like graphql_codegen or the now deprecated artemis).

I think another package is warranted though, as users of built_value probably don't like and additional dependency to freezed which they don't need and visa-versa.

Dependencies that use analyzer are notoriously annoying due to the frequent breaking changes, so they should not be added unless necessary.

Abhilash-Chandran commented 5 months ago

I will give it a try then. 👍