Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GraphQL schema stiching #743

Closed
Type1J opened this issue Aug 27, 2020 · 7 comments
Closed

GraphQL schema stiching #743

Type1J opened this issue Aug 27, 2020 · 7 comments
Labels
enhancement Improvement of existing features or bugfix

Comments

@Type1J
Copy link

Type1J commented Aug 27, 2020

Is your feature request related to a problem? Please describe.
I would like to be able to specify that part of my schema is found on an upstream server, and I'd like it updated when an upstream server updates.

A clear and concise description of what the problem is.
I can't combine my microservices without using Apollo Server.

Describe the solution you'd like
I'd like a way to point to a server to fill in the types that the current server doesn't know about, and then be able to return one of those types in a resolver. I'd also like input types to be proxied through as well.

Describe alternatives you've considered
Apollo Server can do schema stiching, and may be a good starting place for how it should be done in Juniper.

@Type1J Type1J added the enhancement Improvement of existing features or bugfix label Aug 27, 2020
@LegNeato
Copy link
Member

We will likely never support this. Juniper's schema is defined at compile time, not runtime.

@Type1J
Copy link
Author

Type1J commented Aug 30, 2020

@LegNeato It is something that is feasible to do at runtime. The upstream types would be opaque. This is a pass-though feature, not something that would generate types.

@mickdekkers
Copy link

You may be interested in Federation if/when that is implemented in Juniper. From my understanding it does what you describe and although it was started by the Apollo team, there is an open specification for implementing it in different languages.

@LegNeato
Copy link
Member

This doesn't need to be in Juniper though, right? From what I know (not much!) It is probably handled by a higher level that chooses this part of the graph -> call juniper::execute(), other parts of the graph, do X.

Am I thinking about it wrong here?

@mickdekkers
Copy link

@LegNeato I'm afraid I'm not very familiar with Juniper or with the Federation spec, but I'll try to answer your question as best I can. Generally speaking, Federation consists of two parts: a gateway and a number of implementing services. The gateway implements the higher level logic that determines which implementing services to call for each part of the graph, which I think you're referring to.

I think being able to create a Federation implementing service using Juniper would be a great start. This alone would deliver a lot of value as it would let anyone write GraphQL microservices in Rust as part of a Federation graph.

While certainly nice to have, the Federation gateway doesn't need to be implemented in Rust initially. Because Federation is based on a specification, people can use Apollo's Federation Gateway Node.js package until someone decides to implement the gateway spec in Rust. Either way, I agree the gateway probably doesn't need to be in Juniper.

There was some discussion in #376 about what changes to Juniper are required to be able to create a Federation implementing service, specifically these two comments.

@Type1J
Copy link
Author

Type1J commented Sep 1, 2020

@mickdekkers You are correct, I believe Federation is better than stitching, but I didn't know of its existence until your comment. Then I saw this: https://www.youtube.com/watch?v=ra5WuUvQRIM The federation gateway can be a different crate.

@kraem
Copy link

kraem commented Mar 13, 2024

Sorry for necro-bumping this but i've hit this issue as well.

This doesn't need to be in Juniper though, right? From what I know (not much!) It is probably handled by a higher level that chooses this part of the graph -> call juniper::execute(), other parts of the graph, do X.

Am I thinking about it wrong here?

@LegNeato i tried your approach:

  1. First by checking operation_name is some predefined name and passing the query to the remote graphql api.
    But this doesn't allow for the remote query be a subgraph/subquery like so:
    query operation_name {
      entity_from_local_juniper {
        some_field
      }
      entity_from_remote_gql_api {
        some_field
      }
    }
  2. My second approach was parsing the original query, split out the subquery and its arguments and pass them to the remote graphql api. While splitting out the subquery i also remove the subquery from the original query, and calling .execute() on this altered query. I got this approach working and liked it better since i could skip checking the operation_name and serve more dynamic queries like the one from approach 1.
    But if the extracted subquery which is going to the remote graphql is nested inside of some other query it gets a bit more complicated assembling the result:
    query {
      entity_from_local_juniper {
        some_field,
        entity_from_remote_gql_api {
          some_field
        }
      }
    }
    I guess i could save the path to the parent query (if any) and use that while assembling the two responses from the local and remote responses, i haven't had the time to try that approach yet.

It would be a lot more ergonomic if one could mark a resolver to only pass the variables and query along into the resolver to be able to call an upstream graphql api. Since (i guess) it would be hard to have juniper to know about the structure of the remote response due to its code first architecture, maybe it would be possible to get the response as a String, possibly serde_json::Value, and have juniper "inject" it into the response at the right search path.

I'm pretty new to graphql in general so I'm open if someone has a better solution / any other pointers :)

edit: I've implemented storing the path(s) to the remote gql query in the original query to be able to inject the remote response into the right path of the response while combining the results. Surely there are edge cases which i haven't thought about and it doesn't feel very ergonomic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Improvement of existing features or bugfix
Projects
None yet
Development

No branches or pull requests

4 participants