graphql-rust/graphql-client

Quick conversion between GraphQL input types and GraphQL types

Opened this issue · 1 comments

Hi,
I'm facing a weird issue about the input types generated by the GraphQL macro.
Lets say I already have the CardInfo type in my codebase (on the server side), and I use the CardInfo type in my _schema.graphql :

#[derive(/*... */, SimpleObject, InputObject)]
#[graphql(input_name = "CardInfosInput")]
pub struct CardInfos {
// ...
};

_schema.graphql

# ...
type MutationRoot {
  createCard(card: CardInfos!): CardId!
}
# ...

When I use a build script to generate a sdl back from this _schema.graphql which is in a different crate, it creates both a CardInfosInput input and a CardInfo type :

build.rs

let schema = Schema::build(QueryRoot {}, MutationRoot {}, SubscriptionRoot {}).finish();
let mut file = BufWriter::new(File::create(&generated_path)?);

writeln!(&mut file, "{}", schema.sdl())?;

server_sdl.graphql (the generated one)

type CardInfos {
  fullName: FullName!
  jobTitle: JobTitle!
  # ...
}

input CardInfosInput {
  fullName: FullName!
  jobTitle: JobTitle!
  # ...
}

Which forces me to use ...Input types in my GraphQL operations definitions

createCard.graphql

mutation CreateCard($card: CardInfosInput!) {
  createCard(card: $card)
}

This mutation is merged to the MutationRoot, and is defined as such :

#[derive(GraphQLQuery)]
#[graphql(schema_path = "server_sdl.graphql", query_path = "src/queries/createCard.graphql", response_derives = "Debug")]
pub struct CreateCard;

However during the implementation of the resolver of such operations, I figured out there was no way to convert X to XInput types using something like a From or Into trait, in order to only write something like :

pub async fn create_card(state: GraphqlState, card: CardInfos) -> anyhow::Result<CardId> {
  let schema = get_schema();
  let vars = create_card::Variables { card: card.into() };
  // ....
}
impl From<CardInfos> for crate::queries::create_card::CardInfosInput {
  fn from(value: CardInfos) -> Self {
    Self {
      full_name: value.full_name,
      job_title: value.job_title,
      // ...
    }
  }
}

Furthermore, when using nested types I have to write more and more boilerplate.

Is there a way to avoid doing such a thing ? Or maybe could this macro expansion behavior be improved in any way ?

The problem can be summarized as follows :

     (server)                          (client)
StructA => StructAInput => SDL => StructA / StructInputA

Is there a way to reuse backend structs ?

Thanks in advance !

I'd say this is out of scope for this crate, but I am happy to discuss feature requests if there is an elegant mechanism we can implement to make From / Into impls less painful.