Absinthe: Multiple GraphQL Backends

Created on 8 Sep 2017  路  30Comments  路  Source: absinthe-graphql/absinthe

Hi,

I have a micro-service architecture, because that's all we do now :sob:, and I was hoping to run a master/minion configuration of Absinthe.

What do I mean by that?

Master - API Gateway that handles authentication and passes requests to one or more minions for a response

Minion - Exposes a small GraphQL subset

At the moment, I'm starting to write resolvers that go and speak to other Absinthe servers, but I'm hoping Absinthe will provide a more idiomatic way of providing this.

NB: Apollo are working on exactly this and referring to it as "Schema Stiching": https://github.com/apollographql/graphql-tools/pull/382

Feature

Most helpful comment

@binaryseed any update on the open sourcing?

All 30 comments

So I've started to put together a GraphQL resolver, but I'm struggling to reconstruct the query in the resolver to forward it on. Is there a simple way to do this with the context?

Hey! There isn't really any built in feature for this yet, nor are there any "client" type facilities in Absinthe to easily reconstruct queries.

Apollo development just released a very interesting article on GraphQL stitching. It's mainly built on top of their graphql-tools library. Maybe it can serve as reference for a possible implementation here?

@polmiro I have a rudimentary version of this working, using Tesla as a GraphQL client.

At the moment, as @benwilson512 as said, there's no way to reconstruct the query - so I'm just hard-coding the query to request all values. As I said, rudimentary :smile:

defmodule Absinthe.ProxyResolver do
  use Tesla
  adapter :hackney

  def user_from_identifier(identifier, _context) do
    user = request(method: :get, url: "https://user-service:5050/graphql", body: ~s/
      { user(identifier: identifier) {
          forename, surname, email, password_hash
        }
      }
    /)
    |> Map.get(:body)
    |> Poison.decode!([keys: :atoms])

    {:ok, user.data.user}
  end
end

@benwilson512 do you think this "Schema Stiching" capability is something Absinthe will be able to do? We'd love to see this feature, an would like to help in any way possible to make it happen :)

This should be a lot easier after schema definition changes slated for 1.5. We鈥檒l be switching gears to that after 1.4 hits stable.

Awesome!

@bruce Is there a place to see the planned changes for schema definitions?

@zimt28 the planned changes are first and foremost internal. We expect to avoid any breaking changes, all schemas that work today should continue to work. These changes ought to enable a variety of features however, and fix some bugs along the way. https://github.com/absinthe-graphql/absinthe/milestone/7 tracks the issues associated with the change.

@rawkode did you ever enhance your resolver? Or is it still simply requesting all values?

@axelson I'm still using that as is, there's no way to get the query at that stage. I started using Apollo Server, which supports schema stiching, as the API gateway and I am phasing our Absinthe at that layer

@benwilson512 Given that 1.5 seems to be mostly finished, I was wondering if you might have more information on how difficult schema stitching (or something like it) would be to implement in 1.5?

@rawkode could you tell us how was your experience using Apollo Server directly?
Do you miss some Absinthe features or the Elixir power?

Hi, my team at New Relic has schema stitching implemented on top of Absinthe, and I intend to open-source that in the coming months

It's not at all trivial, but we have most of the edge cases worked out

That awesome @binaryseed, if you need some beta testing, at Valiot we use Absinthe for our microservices but an Apollo schema stitcher, we would love to help you and your team.

@binaryseed any update on the open sourcing?

Just wanted to note that Apollo deprecated their stitching solution, in favor of Apollo Federation.

While stitching was compatible with Absinthe backends, it seems like Federation is not, because they require the 'minion' services to expose a different kind of schema. Interested to see how that fits into this discussion and Absinthe.

I was wrong: these fancy new @key things are actually in the GraphQL spec as 'directives', Absinthe has ways to define them, and here is a page describing which ones to support in your Schema to make use of the @apollo/gateway: Federation spec.

Sounds compatible, and also sounds like a thing that could live outside of the main Absinthe repo.

Edit +10 hours: I was wrong again... seems like the directive macro defines directives that can be used in the query, but after a day of hacking away at it I can't figure out how to get them into my Schema. I'll try to leave it alone now and wait for better informed people.

@sebsel did you get anywhere with the federation?

@JakeBeresford No, I did give it up after writing that above reply. We just use stitching and wrote the stitching service in NodeJS.

Federation requires a property on your schema that returns the SDL (query { _service { sdl } }), but since SDL is a feature of Absinthe 1.5, it might be best to wait for that. Unfortunately, it has been in development for quite a while now, as I understand mostly because of reworkings to support the now-deprecated stitching. But having your schema in SDL is beneficial for both.

Also, this is needed, as I mentioned in the earlier post: https://github.com/absinthe-graphql/absinthe/issues/766

Maybe Federation should have it's own issue, as it is quite distinct from what the OP asks for.

@sebsel Great article on Stitching vs Federation! Any updates on getting the Federation setup to work in Absinthe? Would love to be able to use this for a large microservices project my team is getting ready to start on.

Should we create a new issue for Federation Support ?

Official Apollo Federation Spec

Any updates on this? I'm currently working on Apollo Federation support in Absinthe and the main blockers I have at the moment are:

  1. Defining __resolveReference does not work because Absinthe correctly throws an error when using 2 underscores on a field. I'm looking into configuring that error as a warning instead in order to support the federation spec.
  2. extend support is not 100% required but without it Absinthe APIs will not be able to extend types defined in other schemas, only define source types:
    PR to Support extend in Absinthe
  3. I cannot figure out how to _APPLY_ directives to _SCHEMA_ (rather than _QUERIES_). I'm still navigating directives myself but from my learnings I've found 2 distinct ways to _APPLY_ directives:

QUERY DIRECTIVES
These are defined by the client when querying a GraphQL API. Using the directive notation macros in Absinthe will make these directives valid for clients to use in their queries, and then GraphQL clients can use them when querying:

{
    getUser(id: 1) {
       id
       name @skip(if: true)
    }
}

This will add skip to the list of directives on the name field Blueprint, which can then be used in middleware/plugins/phases etc to customize behavior. In this case, the skip behavior is built into Absinthe.

SCHEMA DIRECTIVES
Unlike Query Directives that are applied when querying the api, Schema Directives are _APPLIED_ directly to the schema. This is the case needed for Apollo Federation because the gateway uses these directives to understand how to resolve queries across federated services. The best example is the @key(fields: [_FieldSet]) directive:

directive :key do
  arg :fields, list_of(:_FieldSet)
  on [:object, :interface]

  expand fn
    _, node ->
      Blueprint.put_flag(node, :fields, __MODULE__)
  end
end

object :directive_tester, directives: ["I WANT TO APPLY KEY DIRECTIVE WITH ARGS HERE"] do
  field :id, :string
  field :_resolve_reference, :directive_tester do # should be __resolve_reference with double underscore
    resolve(fn _, _, _ ->
      {:ok, %{ id: 1 }}
    end)
  end
end

It seems that directives is a valid attribute when defining an object, I just cannot figure out what I'm supposed to put there or if it's actually supported. I found this question on the Elixir forum which also seems to express the same confusion:

How to apply directives?

A built-in example of this is the @deprecated(reason: String) directive. Obviously a client would not be specifying this when querying, it is up to the server to _APPLY_ this to the schema.

IMPORTANCE
I know there are some federation/stitching alternatives in the Elixir/Absinthe world, but due to the popularity of Apollo I believe supporting the federation spec may help remove the barriers preventing wider adoption of Elixir/Absinthe, especially if Apollo Federation ends up becoming part of the standard GraphQL spec.

Any guidance on this would be highly appreciated. I have PLENTY of free time right now and would love to learn more about Absinthe and contribute any way I can, including documenting this process.

From what I can tell, Apollo Federation relies on some behavior that "works around" the GraphQL spec itself... That's why it requires the API to expose the SDL of the schema as a field instead of using the normal Introspection query. GraphQL schema directives are intentionally _not_ exposed as part of the normal introspection queries, they are "private" to the server...

I think it'd be interesting to see Federation support as a library separate from Absinthe itself... A library could make modifications the various pipelines inside Absinthe to change the behavior as desired, like the __ field name restriction for example:

https://github.com/absinthe-graphql/absinthe/blob/master/lib/absinthe/phase/schema/validation/type_names_are_reserved.ex

@binaryseed thanks for the prompt reply. I'll look deeper into the exposed SDL, I appreciate the feedback there.

My plan is for this to be a separate library without any need to change Absinthe. I'm hopeful that will be possible due to the various extension points available (plugins, middleware, phases etc).

What kind of work needs to be done to support federation?

Either with Apollo in front, or providing a similar functionality without it.

Is there currently any way to get this working at all?

Any updates on this problem? I feel like this is the main deal-breaker for adopting Elixir and Absinthe for most people.

I've posted this in the various Issues but perhaps not this one... We did open source an example schema stitching application:

https://github.com/newrelic/absinthe-schema-stitching-example

Building this kind of support into absinthe core is unlikely, but with the flexibility of absinthe as-is, it's possible to accomplish.

Closing since this kind of feature won't be built into absinthe core. We'd be excited to see what folks in the community are doing in this space!

Was this page helpful?
0 / 5 - 0 ratings