movio / bramble

A federated GraphQL API gateway
https://movio.github.io/bramble/
MIT License
497 stars 55 forks source link

Refactor merge script to streamline `@boundary` directives #134

Open gmac opened 2 years ago

gmac commented 2 years ago

Bramble's present implementation of the @boundary directive has several indirections, and could be greatly streamlined. Myself and @exterm are considering some uses and would be interested in pursing some refactors of the merge script. While there's discussion of supporting config alternatives to directives, we believe that a refined SDL could work in tandem with this config objective, and improve the overall implementation of the tool in a backwards-compatible manner.

Current SDL limitations

Proposed SDL refactors

We propose remedying these problems by refactoring the @boundary directive and merge script to use the following signature:

directive @boundary(public: Boolean=false, types: [String!]) on FIELD_DEFINITION

With this new signature, the following happens:

  1. The @boundary directive no longer appears on types (we'll just ignore it on types, thus being backwards compatible)
  2. Boundary types are inferred as all types that provide a @boundary query somewhere in the graph, with some rules:
    • A @boundary query that yields a type appearing in only one service isn't actually a boundary; we'll allow the directive though because you may be incrementally rolling out service schemas with this new boundary.
    • A type that contains unique fields beyond just id without a @boundary query is an error (a boundary query is required to access unique data).
    • This pattern allows id-only types to be considered boundaries without needing to provide a useless query.
  3. Setting @boundary(public: true) will include the query in the gateway schema, and omit the boundary directive.
  4. Setting @boundary(types: ["Gizmo", "Gadget"]) will limit the scope of boundary types provided by a query, allowing for boundary queries to (eventually) return abstract types. This is mainly thinking around the scenario where a type may be available through multiple queries in the service, and we want to direct the gateway to a specific query for the type. This is kind of an edge case, and probably doesn't need to be a first-round feature.

All told these rules are backwards compatible, simplify the SDL, and lend themselves to being represented in a configuration-only manner.

Namespace

The downside here is that this does create inconsistencies with the @namespace directive, which I’d probably leave untouched for now. In general, namespace seems finicky to me because it makes assumptions about type root-ness, which isn’t guaranteed in the GraphQL type system (the object type assigned as the root query may again be returned from anywhere in the graph, thus root access is circumstantial).


Curious on @nmaquet and the Movio team’s take on this refactor before pursuing it. This has been gnawing me for a while, and now we have multiple teams prototyping with Bramble. Would be nice to get boundaries spruced up for them.

nmaquet commented 2 years ago

Hey @gmac! Thanks for submitting this. What would you say, as an alternative to modifying the existing @boundary directive, to introduce a new one, as follows:

directive @getter(public: Boolean, types: [String!]) on FIELD_DEFINITION

In this way, we can eventually deprecate @boundary without any potentially confusing or surprising overlap. Also, I think the name boundary makes sense when referring to a type, but it has always felt a bit weird to add it to a field definition. The name getter (implicitly boundary type getter) makes more sense IMHO.

Other than that, I support the change, which, as you said has quite a number of advantages:

gmac commented 2 years ago

Sounds good!

gmac commented 1 year ago

FWIW, I ended up doing pretty much this implementation for Ruby schema stitching: https://github.com/gmac/graphql-stitching-ruby. Huge compliments to Bramble, you folks provided a lot of inspiration for that project.

nmaquet commented 1 year ago

@gmac Wow, what a cool project! Thanks for sharing and for the shoutout. Multi-key support, configurable directive name, implicit boundary type declaration, support for both schema-first and code-first schemas, ... this looks really comprehensive. Very impressive!

gmac commented 1 year ago

@nmaquet thanks! Under the hood, it uses flat json-based delegation mapping with a very similar schema to Bramble; I really like how you folks did that. The skeleton of the planner is also very much Bramble. It pulls in GraphQL Tools ideas for remote delegation strategies where overlapping locations are permitted. I was looking to hit the simplicity of Bramble with some more of the flexibility in Tools. Fun new toy for our Ruby ecosystem.

That said, I am working on the official Shopify gateway these days and alas we went with Rust. I do know Bramble got some use in one of our smaller API offerings though. Hope all is well with your team. Still a big fan of your work and I keep an eye on what you’re up to over here.