juniper icon indicating copy to clipboard operation
juniper copied to clipboard

Are subscriptions limited to being top level?

Open jerel opened this issue 3 years ago • 3 comments

I think this is a bug but I'm not sure... I've spent several days reading code and trying in vain to achieve this so I'd like a second opinion.

Queries can be set up like this (and so can mutations):

pub struct QueryRoot;

#[juniper::graphql_object(Context = Context)]
impl QueryRoot {
  fn settings(&self) -> settings::SettingQueries {
    settings::SettingQueries
  }
  fn other(&self) -> other::OtherQueries {
    other::OtherQueries
  }
}

However, no variation of this seems to be possible with Subscriptions (this doesn't compile):

pub struct SubscriptionRoot;

#[juniper::graphql_subscription(Context = Context)]
impl SubscriptionRoot {
  fn settings(&self) -> settings::SettingSubscriptions {
    settings::SettingSubscriptions
  }
  fn other(&self) -> other::OtherSubscriptions {
    other::OtherSubscriptions
  }
}

Shouldn't this work or is it a design decision? Are all subscriptions for the whole app required to be in one root impl?

jerel avatar Apr 02 '21 22:04 jerel

What is the compilation error you are getting? I didn't work on the subscriptions work but might be able to answer the question.

LegNeato avatar Apr 04 '21 01:04 LegNeato

Thanks @LegNeato, I should have added all this to the original issue. I'll show a couple of the angles that I've tried and their errors:

// this is identical to my usage of the Query/Mutation API but with s/graphql_object/graphql_subscription/ and async fns
pub struct SubscriptionRoot;

#[juniper::graphql_subscription(Context = Context)]
impl SubscriptionRoot {
  async fn settings(&self) -> settings::SettingSubscriptions {
    settings::SettingSubscriptions
  }
  async fn other(&self) -> other::OtherSubscriptions {
    other::OtherSubscriptions
  }
}

pub struct SettingSubscriptions;
#[juniper::graphql_subscription(Context = Context)]
impl SettingSubscriptions {
  pub async fn settings() ...
}

gives this error at the SubscriptionRoot site:

#[juniper::graphql_subscription(Context = Context)]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `futures::Stream` is not implemented for SettingSubscriptions

So I thought "maybe we can define the top level as a graphql_object which then pulls in the graphql_subscription":

pub struct SubscriptionRoot;

#[juniper::graphql_object(Context = Context)]
impl SubscriptionRoot {
  fn settings(&self) -> settings::SettingSubscriptions {
    settings::SettingSubscriptions
  }
  fn other(&self) -> other::OtherSubscriptions {
    other::OtherSubscriptions
  }
}

pub struct SettingSubscriptions;
#[juniper::graphql_subscription(Context = Context)]
impl SettingSubscriptions {
  pub async fn settings() ...
}

gives this error at the SubscriptionRoot site:

#[juniper::graphql_object(Context = Context)]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `GraphQLValueAsync<__S>` is not implemented for SettingSubscriptions

Which is understandable I suppose because we're defining SubscriptionRoot as a graphql_object. I thought perhaps this was a way that could work today in userland code but I wasn't able to get GraphQLValueAsync<__S> defined correctly for some reason.

jerel avatar Apr 05 '21 15:04 jerel

@jerel as far as I recall, this is not supported at the moment. The reason is that is not clear how such situations should be resolved. A subscription in a nutshell is a Stream. So having a Stream, whcih field resolves to another Stream, how you expect to behave this on client?

Do you have any reference examples how it's handled in other languages?

tyranron avatar Jul 19 '21 11:07 tyranron