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

Feature Request: Support Apollo Federation #64

Closed
StevenACoffman opened this issue Jul 9, 2019 · 9 comments
Closed

Feature Request: Support Apollo Federation #64

StevenACoffman opened this issue Jul 9, 2019 · 9 comments

Comments

@StevenACoffman
Copy link

StevenACoffman commented Jul 9, 2019

Feature Request:

Please consider supporting Graphql Federation. If this could function as a gateway which composes the overall schema and executes federated queries, that would be amazing. There is work ongoing to add federation support for gqlgen.

Apollo Federation is made up of two parts:

  • Federated services, which are standalone parts of the graph
  • A gateway which composes the overall schema and executes federated queries

To be part of a federated graph, a microservice implements the Apollo Federation spec which exposes its capabilities to tooling and the gateway. The federated service can extend types from other services and add its own types that can be extended.

Collectively, federated services form a composed graph. This composition is done by a gateway which knows how to take an incoming operation and turn it into a plan of fetches to downstream services. The gateway orchestrates requests, merges the data and errors together, and forms the overall result to send back to the client.

Background:

We use hundreds of microservices, and a monolithic GraphQL server becomes an unacceptable development bottleneck and single point of failure, so it becomes necessary to divide the graph's implemention into separate parts. We tried schema stitching, but would prefer federation for three reasons:

  1. With federation, microservices all expose a proper part of the overall graph and can refer directly to types that live in other services, without the need to add foreign keys or superfluous relationship root fields to your schema.
  2. Federation is fully declarative and doesn't require any user code to be running in the gateway.
  3. Execution is efficient and predictable, because it relies on a query plan generated ahead of time with full knowledge of the overall query, instead of on runtime schema delegation that gets invoked as part of normal resolver-based execution.
@AlecAivazis
Copy link
Member

Hey @StevenACoffman! Thanks for opening this issue.

I have been closely watching the development of the Apollo federated gateway since Martjin showed it at GraphQL summit last year. I think its important to distinguish "schema federation" as a concept from the particular implementation that was released by the Apollo team. This library provides a federated gateway that works without adding any additional constraints to the underlying services.

On the other hand, the Apollo Federated Gateway requires a lot from the backing services in order to work. While I'm sure they had good motivations for their design choices, I am trying my hardest to ensure that users can experiment with schema federation without having to change their existing services. There are a few different reasons for this but they all essentially boil down to wanting to avoid issues like the one you shared on gqlgen, or similar libraries. Since the community is clearly still experimenting with how to handle distributed APIs, it feels wrong to go impose a particular system on the entire community when a better solution might still be out there. On top of the burden on the wider community, something feels very weird to me about decorating a local schema with orchestration information.

The nautilus gateway avoids these issues by adopting a more convention-driven approach that is less flexible but takes advantage of community best practices (such as the Nodeinterface) to do its job. My hope is that this library can exist as a solution for users who are willing to play by a simple set of rules that don't rely on special support from whatever server-side library you are working with.

At the moment, I don't have any plans to support services that deviate from this libraries conventions by taking advantage of the tweaking that the Apollo Gateway allows. I think they are too flexible and risk bringing us into the same issues we faced with schema stitching. That being said, if there are features that the Apollo federated gateway supports that nautilus does not, I would be very happy to add them!

@StevenACoffman
Copy link
Author

StevenACoffman commented Jul 12, 2019

Thank you for your response. I’ve been chewing on it for a bit, and I have a follow-up question.

Many times our permissions (authorization) checks for fields in one service require fetching data from a separate service. I would prefer to avoid having each resolver do the work of looking up the permission because it would be:
a. much slower, since it happens in serial
b. super wasteful, since multiple resolvers often need the same permissions data.

Here is an example of what I was thinking of:

# article.schema
type Article {
    id: String
    articleContent: Content  @accessCurrentUserMustBeEntitledOrInternalEmployee
    title: String
    … Other public metadata
}

I’m using directives on fields to assign permissions, so partial results can be returned for users that have access to other fields, like public metadata.

I want to federate our queries across two services, one for authorization (entitlement), and one for content + metadata.

When the gateway gets a query something like:

query article($articleIds) { getArticles(articleIds) { articleContent } }

In theory, we can pass the client user id, as well as the article ids, but it seems sort of off.

@AlecAivazis
Copy link
Member

Hey @StevenACoffman thanks for the follow up. I am a bit confused how this relates to the previous post 🤔

It sounds like you want a way for directives to modify the query plan generated by the Planner. At the moment that's not possible but if you wanted to open a separate issue with your usecase above, I would love to look into it when I get some down time.

@StevenACoffman
Copy link
Author

Yeah, sorry to kind of hijack the issue. The relevant bit I was struggling to articulate was that Authorization tends to be such an important cross cutting concern that at the federated gateway it suggests somehow sharing context or hinting to the gateway. We can also put permissions into initial login jwt token that gets passed around, or make queries require permission as parameters.

e.g. query article($articleIds, $userContext) { getArticles(articleIds, userContext) { articleContent } }

@AlecAivazis
Copy link
Member

Yea I definitely see the need for this kind of modification - mind opening a ticket? If you wanted to take a stab at implementing I'd love to provide any guidance you need

@jakubknejzlik
Copy link

@AlecAivazis I agree with your opinion in all points. On the other hand I often myself in situations, where I don't want to create separate logic just to merge multiple services/schemas together and link specific types to specific fields. I've created method for stitching things together by providing type extensions with special directives to gateway, but the downside was that I always had to redeploy the gateway with updated stitching.

For example:

extend type Comment {
  createdByUser: User @link(fetchField: "user", reference: "createdBy")
}

Basically the gateway just fetched it by delegating to same schema this query:

query {
  user(id:"{{createdBy}}"): { ... }
}

But this is very basic functionality...that's why I was thrilled while watching the Federation integration :)

Do you think that creating directives that could be parsed from schema (from underlying services) could handle the logic of stitching. I think it should be possible without the need of modifying the current implementation, although being able to integrate it with the query planner would have some major benefits.

Also to support Apollo Federation all you have to do is expose _service: _Service! field and type on query so it's not a big deal for underlying services :) (of course if you need to support reference resolver, you have to implement the _entities: [_Entity]! field/union)

@AlecAivazis
Copy link
Member

Hey @jakubknejzlik!

@StevenACoffman opened another issue that's focused on auth and gateway-level directives so I copied your comment and replied over there - hope that's okay!

@aquiseb
Copy link

aquiseb commented Sep 20, 2019

@jakubknejzlik Would you please have an example of your method for stitching things together by providing type extensions with special directives to gateway?

@StevenACoffman
Copy link
Author

@AlecAivazis Just so you are aware, an engineer from Tyk created an alternative schema federation solution graphql-gateway and has a comparison with the Nautilus Gateway

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants