Skip to content
\n

Here is my code:

\n
#[derive(GraphQLInputObject, Serialize, Debug)]\npub struct ConnectionParams<T> {\n\tpub after: Option<String>,\n\tpub first: Option<i32>,\n\tpub params: Option<T>\n}\n
\n

and a sample of one of the structs that will be used for type T (ie ConnectionParams<UserParams>):

\n
#[derive(GraphQLInputObject, Serialize, Debug)]\npub struct UserParams {\n\tpub username: Option<StringFilter>,\n\tpub is_employee: Option<bool>,\n}\n
\n

How do I correctly do this?

","upvoteCount":1,"answerCount":2,"acceptedAnswer":{"@type":"Answer","text":"

@bnhardy yup, at the moment GraphQLInputObject macro cannot work with generics. Unfortunately, even when it will be able to, it won't allow your use case anyway.

\n

What you're trying to reach here is a bit fundamentally incorrect, because GraphQL types system has no notion of generics at all. Try to express the same thing using SDL (GraphQL schema definition language), and you won't be able to.

\n

So... what happens, actually?

\n

When you write:

\n
#[derive(GraphQLInputObject, Serialize, Debug)]\npub struct ConnectionParams<T> {\n    pub after: Option<String>,\n    pub first: Option<i32>,\n    pub params: Option<T>,\n}
\n

You define in GraphQL schema a ConnectionParams input object like this:

\n
input ConnectionParams {\n  after: String\n  first: Int\n  params: ???   # what type should be here?\n}
\n

We should know the params GraphQL type at the definition place, otherwise we cannot construct the ConnectionParams GraphQL type definition.

\n

Then, let's place UserParams into T, so we have params field type being UserParams. This way, we can write (theoretically, though we don't have compile-time introspection in Rust) the GraphQL type definition for the ConnectionParams<UserParams> Rust type like this:

\n
input ConnectionParams {\n  after: String\n  first: Int\n  params: UserParams\n}
\n

It's OK, but then we want to use a ConnectionParams<PaymentParams>, for example, so it should be:

\n
input ConnectionParams {\n  after: String\n  first: Int\n  params: PaymentParams\n}
\n

Ooops! We cannot have the same GraphQL type declared twice with the same name but different fields!

\n

To have this, we should do the monomorphization of \"generic\" GraphQL type by hand and specify different names for different types:

\n
input UserConnectionParams {\n  after: String\n  first: Int\n  params: UserParams\n}\ninput PaymentConnectionParams {\n  after: String\n  first: Int\n  params: PaymentParams\n}
\n

So, unfortunately, at the moment, you don't have any other options rather than duplicating declarations in Rust:

\n
#[derive(GraphQLInputObject, Serialize, Debug)]\npub struct UserConnectionParams {\n    pub after: Option<String>,\n    pub first: Option<i32>,\n    pub params: Option<UserParams>,\n}\n#[derive(GraphQLInputObject, Serialize, Debug)]\npub struct PaymentConnectionParams {\n    pub after: Option<String>,\n    pub first: Option<i32>,\n    pub params: Option<PaymentParams>,\n}
\n

In future, perhaps, we will be able to support some kind of templating GraphQL types from Rust generic types. Though, being somewhat an obvious feature, it's far from being trivial to implement, considering it has a solid design and good ergonomics, as would require to build a rich compile-time introspection system, which may end very fast with Rust not being able to express something. And, also, templating doc comments and type names maybe quite a weird feature for a rustacean.

\n

If you're too concerned with a code repetition, you may declare a small declarative macro for defining such ConnectionParams structs, like the one bellow:

\n
macro_rules! connection_params {\n    ($name:ident, $params:type) => {\n        #[derive(GraphQLInputObject, Serialize, Debug)]\n        pub struct $name {\n\t    pub after: Option<String>,\n            pub first: Option<i32>,\n\t    pub params: Option<$params>,\n        }\n    }\n}\n\nconnection_params!(UserConnectionParams, UserParams)\nconnection_params!(PaymentConnectionParams, PaymentParams)
","upvoteCount":1,"url":"https://github.com/graphql-rust/juniper/discussions/1127#discussioncomment-4229383"}}}
Discussion options

You must be logged in to vote

@bnhardy yup, at the moment GraphQLInputObject macro cannot work with generics. Unfortunately, even when it will be able to, it won't allow your use case anyway.

What you're trying to reach here is a bit fundamentally incorrect, because GraphQL types system has no notion of generics at all. Try to express the same thing using SDL (GraphQL schema definition language), and you won't be able to.

So... what happens, actually?

When you write:

#[derive(GraphQLInputObject, Serialize, Debug)]
pub struct ConnectionParams<T> {
    pub after: Option<String>,
    pub first: Option<i32>,
    pub params: Option<T>,
}

You define in GraphQL schema a ConnectionParams input object like this:

input Connecti…

Replies: 2 comments 1 reply

Comment options

You must be logged in to vote
1 reply
@twiclo
Comment options

Answer selected by bnhardy
Comment options

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
Q&A
Labels
3 participants