gusto / apollo-federation-ruby Goto Github PK
View Code? Open in Web Editor NEWA Ruby implementation of Apollo Federation
License: MIT License
A Ruby implementation of Apollo Federation
License: MIT License
From the known issues and limitations section in the README:
- Currently only works with class-based schemas
It makes perfect sense that you'd implement this for class-based schemas first, since that seems to be the future of graphql-ruby
. Are there any plans to support the .define
-style schemas as well?
I have a large .define
-style schema and I'd love to be able to use this gem with it!
Apollo documentation describes how to reference an entity without contributing fields. The resolvable
argument on the key
field indicates to Apollo that the subgraph is only referencing the entity and has no resolver.
I looked at the key implementation here and didn't see a way set this argument in my subgraph.
Could this be added in order to fulfill this aspect of federation?
Schema Example
type Product @key(fields: "id", resolvable: false) {
id: ID!
}
In a Rails app with a controller that supports query-batching via multiplex, enabling federated tracing (using the documented steps) leads to a runtime error when processing batched queries.
Controller action:
def execute
context = {
tracing_enabled: ApolloFederation::Tracing.should_add_traces(request.headers),
}
multi = params[:_json]
result =
if multi
MySchema.multiplex(
multi.map do |param|
{
query: param[:query],
operation_name: param[:operationName],
variables: prepare_variables(param[:variables]),
context: context
}
end
)
else
MySchema.execute(
params[:query],
operation_name: params[:operationName],
variables: prepare_variables(params[:variables]),
context: context
)
end
render json: result
end
The following requests succeed:
# single query
$ curl localhost:3000/graphql -H 'content-type: application/json' -d '{"query":"query{__schema{__typename}}"}'
{"data":{"__schema":{"__typename":"__Schema"}}}
# batch of 1
$ curl localhost:3000/graphql -H 'content-type: application/json' -d '[{"query":"query{__schema{__typename}}"}]'
[{"data":{"__schema":{"__typename":"__Schema"}}}]
The following request fails:
# batch of 2
$ curl localhost:3000/graphql -H 'content-type: application/json' -d '[{"query":"query{__schema{__typename}}"},{"query":"query{__schema{__typename}}"}]'
Completed 500 Internal Server Error in 1831ms (Allocations: 7241)
NoMethodError (undefined method `context' for nil:NilClass):
apollo-federation (3.0.0) lib/apollo-federation/tracing/tracer.rb:87:in `execute_query_lazy'
apollo-federation (3.0.0) lib/apollo-federation/tracing/tracer.rb:48:in `trace'
graphql (2.0.5) lib/graphql/tracing.rb:82:in `call_tracers'
graphql (2.0.5) lib/graphql/tracing.rb:66:in `trace'
graphql (2.0.5) lib/graphql/execution/interpreter.rb:71:in `sync_lazies'
graphql (2.0.5) lib/graphql/execution/interpreter.rb:32:in `finish_multiplex'
graphql (2.0.5) lib/graphql/execution/multiplex.rb:88:in `block (3 levels) in run_all'
graphql (2.0.5) lib/graphql/dataloader.rb:181:in `block in run'
graphql (2.0.5) lib/graphql/dataloader.rb:303:in `block in spawn_fiber'
I don't know the inner workings well enough to connect all the dots, but here a few relevant pieces of code I plucked from the backtrace.
apollo-federation-ruby/lib/apollo-federation/tracing/tracer.rb
Lines 85 to 86 in 4a78028
data[:query]
is nil
(whereas data[:multiplex]
is present).
if query.nil? && multiplex.queries.length == 1
query = multiplex.queries[0]
end
This seems to explain why the error is not thrown for a batch of 1.
The specific problematic bit is that it adds -x86_64-linux
to the google-protobuf gem. Here is an example commit.
In federation version 2 the directives have namespaces (federation__
by default). Currently this prefix is non-configurable which may cause issues when other subgraphs use a different namespace or import them without a namespace.
In Apollo studio I got the following error:
The federation "@inaccessible" directive is imported with mismatched name between subgraphs: it is imported as "@inaccessible" in subgraphs "X" and "Y" but "@federation__inaccessible" in subgraphs "A" and "B"
In this case, A and B are ruby applications, and X and Y are not.
It can be partially fixed by configuring the graph as v1 and overriding Schema#federation_dsl
to prepend an import for all directives. But it's not very elegant ๐ฌ
In my schema I use use ApolloFederation::Tracing
.
My controller method looks like this:
def execute
context = {
request: request,
current_user: current_user,
tracing_enabled: ApolloFederation::Tracing.should_add_traces(request.headers),
}
if params[:_json]
queries = params[:_json].map do |param|
{
query: param[:query],
operation_name: param[:operationName],
variables: ensure_hash(param[:variables]),
context: context,
}
end
result = ApiSchema.multiplex(queries)
else
result = ApiSchema.execute(
params[:query],
operation_name: params[:operationName],
variables: ensure_hash(params[:variables]),
context: context,
)
end
render json: ApolloFederation::Tracing.attach_trace_to_result(result), root: false
end
Every now and then I see a ApolloFederation::Tracing::NotInstalledError
raised. At first I thought maybe it's the multiplexing that causing issues, but the errors are tripped for a single query.
I can also confirm that there are queries getting detailed traces in Apollo Graph Manager, so some of them are definitely working.
Hello Everyone ๐,
We are using type PageInfo
in two subgraphs
using federation 2.0
connection_type
shareable: true
also.# query_type.rb
#...
field :users, UserType.connection_type, 'List all users.' #, shareable: true
def users
User.all
end
INVALID_FIELD_SHARING
Non-shareable field "PageInfo.endCursor" is resolved from multiple subgraphs: it is resolved from subgraphs "channels" and "users" and defined as non-shareable in all of them
INVALID_FIELD_SHARING
Non-shareable field "PageInfo.hasNextPage" is resolved from multiple subgraphs: it is resolved from subgraphs "channels" and "users" and defined as non-shareable in all of them
# base_connection.rb
module Types
class BaseConnection < Types::BaseObject
# add `nodes` and `pageInfo` fields, as well as `edge_type(...)` and `node_nullable(...)` overrides
include GraphQL::Types::Relay::ConnectionBehaviors
end
end
Apollo Federation v2.1 introduced new @composeDirective
directive that allows users to specify directives that should be preserved in the supergraph composition (by default composition strips out most directives from supergraph).
extend schema
@link(
url: "https://specs.apollo.dev/federation/v2.3",
import: ["@composeDirective", "@extends", "@external", "@key", "@inaccessible", "@interfaceObject", "@override", "@provides", "@requires", "@shareable", "@tag"]
)
@link(url: "https://myspecs.dev/myCustomDirective/v1.0", import: ["@custom"])
@composeDirective(name: "@custom")
// will be present in the supergraph definition
directive @custom on OBJECT
type Product @custom @key(fields: "id")
id: ID!
// other fields omitted for clarity
}
Additional resources:
New directive functionality can be tested using Apollo Federation Subgraph Compatibility NPX script (and Github Action). Example integration project is already provided in the subgraph compatibility testing repository.
Do you have any Federation questions? Join Apollo Discord community.
we recently upgraded from 1.1.5
to 2.2.0
and started getting uninitialized constant GraphQL::Define
errors in our specs.
as far as i can tell that class no longer exists in the graphql gem:
https://github.com/rmosolgo/graphql-ruby/search?q=InstanceDefinable
๐ Hello, thank you for this fantastic contribution to graphql ruby.
Is this system actually in "beta" mode as the README state? I have used this in a relatively complex scenario and it seems to be fully spec compliant.
If that is the case, would you accept a PR to remove the beta declaration out of the README?
Thanks again!
I'm trying to integrate this library to my rails apps in graphql, apparently everything is fine, but the gateway gives an error syntax Error: Expected Name, found"} ".
from the console of my rails applications, I don't see much detail to help me
this same gateway file, the poor thing with other graphql services made in nodejs and everything fine
main
branch failed. ๐จI recommend you give this issue a high priority, so other packages depending on you could benefit from your bug fixes and new features.
You can find below the list of errors reported by semantic-release. Each one of them has to be resolved in order to automatically publish your package. Iโm sure you can resolve this ๐ช.
Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.
Once all the errors are resolved, semantic-release will release your package the next time you push a commit to the main
branch. You can also manually restart the failed CI job that runs semantic-release.
If you are not sure how to resolve this, here is some links that can help you:
If those donโt help, or if this issue is reporting something you think isnโt right, you can always ask the humans behind semantic-release.
semantic-release cannot push the version tag to the branch main
on the remote Git repository with URL https://[secure]@github.com/Gusto/apollo-federation-ruby.git
.
This can be caused by:
Good luck with your project โจ
Your semantic-release bot ๐ฆ๐
Sometimes we can forget to regenerate appraisal bundle lockfiles or it could be a local machine difference. Let's consider adding a CI step that verifies that the there are no diffs between the lockfiles.
Hey there!
I've just started playing with this gem, so far so good, tks for getting this released ๐บ While I was experimenting with things, I tried to update to the recently released graphql 1.10.0 but found out that this gem is not compatible with it.
Was there a specific reason for locking the dep to 1.9.x? I haven't gone through the sources of this project yet and there are some breaking changes on 1.10.0 (and it is also supposed to yield faster boot times, which is why we were considering upgrading it โก )
We define it on def to_graphql
in HasDirectives
. Currently deprecation warnings are silenced.
https://github.com/rmosolgo/graphql-ruby/blob/master/CHANGELOG.md#deprecations-2
I think this is only needed to support GraphQL 1.10 and 1.11. We could just delete this method eventually and drop support for the older versions when warnings are no longer silenced.
#105 introduced new behavior that allows us to specify fields as symbols and automatically have the camelcased (e.g. :product_id
becomes productId
). In order to keep the change backwards compatible, the behavior of Strings remains unchanged. This means that there is inconsistent behavior between strings and symbols.
It would be great it, instead, we also camelize string input and give a global configuration option to either disable camelcase. This behavior would also be more inline with how graphql-ruby behaves.
Seems like the last release was October 2020.
When trying to do rails:db migrate, I encounter this error message due to apollo-federation gem:
rails aborted!
LoadError: cannot load such file -- google/protobuf_c
google/protobuf is the issue, but not sure how to solve this
protocolbuffers/protobuf#8062
main
branch failed. ๐จI recommend you give this issue a high priority, so other packages depending on you could benefit from your bug fixes and new features.
You can find below the list of errors reported by semantic-release. Each one of them has to be resolved in order to automatically publish your package. Iโm sure you can resolve this ๐ช.
Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.
Once all the errors are resolved, semantic-release will release your package the next time you push a commit to the main
branch. You can also manually restart the failed CI job that runs semantic-release.
If you are not sure how to resolve this, here is some links that can help you:
If those donโt help, or if this issue is reporting something you think isnโt right, you can always ask the humans behind semantic-release.
A gem host API key must be created and set in the GEM_HOST_API_KEY
environment variable on you CI environment.
You can retrieve an API key either from your ~/.gem/credentials
file or in your profile in RubyGems.org.
Good luck with your project โจ
Your semantic-release bot ๐ฆ๐
master
where our default branch is main
main
branch builds seem to be failing ๐I see the gem is still in the beta stage. Is there a roadmap to release? We would really like to leverage this in a production capacity :)
Hi all,
I want to generate a federated schema. But I get the schema without any directives/etc.
for instance, I have
class UsersSchema < GraphQL::Schema
include ApolloFederation::Schema
orphan_types Types::UserType
end
module Types
class UserType < Types::BaseObject
extend_type
key fields: 'id'
field :id, ID, null: false, external: true
end
end
when I use a general way to generate schema into SDL, JSON, I get an incorrect federated schema
type User {
id: ID!
}
type Query {
_entities(representations: [_Any!]!): [_Entity]!
_service: _Service!
}
scalar _Any
union _Entity = User
"""
The sdl representing the federated service capabilities. Includes federation
directives, removes federation types, and includes rest of full schema after
schema directives have been applied
"""
type _Service {
sdl: String
}
How can I generate a federated schema?
Tracking some feedback from Apollo on the java implementation that's also relevant to this implementation.
Base64.strict_encode64
This leaves out newlines in the middle which aren't particularly helpful when encoded in a JSON string, and causes Node to have to use a slightly slower decode implementation.
Howdy! We updated this gem to 3.4.1 in one of our subgraphs, and its schema verification is failing in Apollo Studio. Here are some results:
Generated Schema snippet:
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible"])
type Something @federation__extends @federation__key(fields: "id") {
id: ID! @federation__external
...
}
Apollo Studio Check output:
UNKNOWN_FEDERATION_LINK_VERSION
[x-service] Invalid version v2.3 for the federation feature in @link direction on schema
INVALID_GRAPHQL
[x-service] Unknown directive "@federation__external".
INVALID_GRAPHQL
[x-service] Unknown directive "@federation__extends".
INVALID_GRAPHQL
[x-service] Unknown directive "@federation__key".
Our supergraph is configured in Apollo Studio to use Federation version 2.0.
Our subgraphs also specify the Federation version using this gem. It seems like that version specification is being ignored when generating the @link
directive.
I'm looking at this code change ๐ c7b987d#diff-48d1e38dfab06335bc60255c48ef179d40143d14e29cfb52673b258346799900R64
Shouldn't that be honoring the federation_version
value?
Given a schema call with context
result = schema.execute(
query,
operation_name: operation_name,
variables: vars,
context: { current_user: 'Bob' }
)
{:query=>"query GetServiceDefinition { _service { sdl } }", :operationName=>nil, :vars=>{}, :schema=>ProductSchema}
will result in
ERROR ArgumentError: unknown keyword: current_user
Given a token we include the current user in all contexts. I would not expect this to be an issue however.
Just found out that the tracing part of this gem is not compatible with google-protobuf
v3.15.3 and up. It can be reproduced by updating the version to 3.15.3
in
bundle update google-protobuf
, and then run rspec spec/apollo-federation/tracing_spec.rb
.
We would then get the following error:
1) ApolloFederation::Tracing with the legacy runtime behaves like a basic tracer respecting options on context adds the extensions.ftv1 when the context has tracing_enabled: true
Failure/Error: ROOT_KEY => ApolloFederation::Tracing::Node.new,
NoMethodError:
undefined method `new' for ApolloFederation::Object:Module
Shared Example Group: "a basic tracer" called from ./spec/apollo-federation/tracing_spec.rb:573
# eval:1:in `initialize'
# ./lib/apollo-federation/tracing/node_map.rb:21:in `initialize'
# ./lib/apollo-federation/tracing/node_map.rb:21:in `new'
# ./lib/apollo-federation/tracing/node_map.rb:21:in `initialize'
# ./lib/apollo-federation/tracing/tracer.rb:76:in `new'
# ./lib/apollo-federation/tracing/tracer.rb:76:in `start_trace'
# ./lib/apollo-federation/tracing/tracer.rb:61:in `block in execute_multiplex'
# ./lib/apollo-federation/tracing/tracer.rb:61:in `each'
# ./lib/apollo-federation/tracing/tracer.rb:61:in `execute_multiplex'
# ./lib/apollo-federation/tracing/tracer.rb:46:in `trace'
The specs are passing for google-protobuf v3.15.2
so I'm guessing it might be caused by this commit protocolbuffers/protobuf@9879f42#diff-b9138194ffe9e7c8bb6d79d1ed56259553d18d9cb60b66e3ba5aa2e5b078055a. I believe this is the only commit between 3.15.2
and 3.15.3
.
I'm not familiar with protobuf
at all so any hints on how this could be fixed will be greatly appreciated. Thanks!
GraphQL Ruby has support for client directives now. It could potentially reduce the surface area of this library by quite a bit.
We have quite a bit code that seems to be responsible for generating the SDL.
Hey, guys. How you are dealing with publishing your services schemas to the gateway? Are you using something similar to schema registration?
Hello Gusto,
I'm facing an issue when using the latest Graphql gem version.
It can be reproduced by changing the graphql version to (1.13.4 or 1.13.5)
rspec ./spec/apollo-federation/entities_field_spec.rb:72
Thanks
Thanks for this gem, I think it will do just what I need. I've added the gem to my app and followed the steps for updating my schema and classes. Is there any documentation in regard to how I should test to confirm my changes?
Firstly, thank so much for the gem!
We consider to use this gem, but the last update is 2020/10 and seem there are no activities since that time. Is there a plan to continue maintain this gem?
Hi, I am trying to use apollo-fderation-ruby in my project.
After I added key keyword. 'bundle exec srb tc' check will fail as below. Any Idea how to resolve this problem.
Quote:
app/graphql/types/user_type.rb:10: Method key does not exist on T.class_of(Types::UserType) https://srb.help/7003
10 | key fields: :id
END.
include ApolloFederation::Schema
...
mutation(Types::Mutation)
query(Types::Query)
...
end
Without the query() things work fine.
Once I add the query() i get the below error
TypeError: Cannot read property 'types' of undefined
at buildClientSchema
(http://localhost:3001/assets/graphiql/rails/application-6f03c7a92432765b0fbe671bfb7a292ee6c37e6704b6e6ac95587e45676bbf72.js:34102:72)
at http://localhost:3001/assets/graphiql/rails/application-6f03c7a92432765b0fbe671bfb7a292ee6c37e6704b6e6ac95587e45676bbf72.js:2795:55
Please advise
P.S.: I'm very new to graphql. Might be missing out on something
It's important that when a subgraph service receives an _entities
query from the gateway that it can resolve all of the references efficiently. You would expect that using a data loader inside the resolve_reference
method for each entity would achieve this but unfortunately it does not work as expected. Currently the data loader is being invoked for each individual reference rather than as a single batch.
I've created a small example application here: https://github.com/col/apollo-federation-example
Expected Output:
Product.resolve_reference reference: {:__typename=>"Product", :id=>"1"}
Product.resolve_reference reference: {:__typename=>"Product", :id=>"2"}
ProductsByUpc.fetch ["1", "2"]
Actual Output:
Product.resolve_reference reference: {:__typename=>"Product", :id=>"1"}
ProductsByUpc.fetch ["1"]
Product.resolve_reference reference: {:__typename=>"Product", :id=>"2"}
ProductsByUpc.fetch ["2"]
At scale this becomes a significant problem as we can receive hundreds of references in a single _entities
query.
Currently only one reference can be loaded at a time, and because the type is attached to the context, lazy objects can't be used.
Some ideas:
ApolloFederation::Entity.resolve_type
..resolve_references(references, context)
method that can load multiple references at the same time.I think idea 2 would be pretty straight forward to implement and a quick stopgap until idea 1 can be implemented.
Any thoughts?
I have been trying to get this working for the past day and have hit a blocker. Any help would be appreciated.
I have created a dummy service - customer service (in nodejs) - and another dummy service - schedule service (in ruby). I want to create a one to many relationship between customer and schedule. i.e. a customer can have many schedules.
I have managed to get queries like the following working
{
schedules {
id
time
customer {
id
schedules {
id
time
customer {
id
}
}
}
}
}
So I feel like I have setup everything up in my service correctly. When I call things in the gateway things partially work as well. For example
{
customers {
id
name
# schedules {
# id
# # time
# }
}
schedules {
id
time
customer {
id
name
schedules {
id
}
}
}
}
As soon as I add in the commented out bit I get the following error.
{
"errors": [
{
"message": "500: Internal Server Error",
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"response": {
"url": "http://localhost:3000/graphql",
"status": 500,
"statusText": "Internal Server Error",
"body": {
"error": {
"message": "no implicit conversion of #<Class:0x00007f81638ce970> into Hash",
"backtrace": [
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/schema/field.rb:524:in `block in resolve'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/schema/field.rb:661:in `block in with_extensions'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/schema/field.rb:680:in `block (2 levels) in run_extensions_before_resolve'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/schema/field.rb:683:in `run_extensions_before_resolve'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/schema/field.rb:680:in `block in run_extensions_before_resolve'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/schema/field_extension.rb:50:in `resolve'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/schema/field.rb:678:in `run_extensions_before_resolve'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/schema/field.rb:660:in `with_extensions'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/schema/field.rb:504:in `resolve'",
"/Users/ryanme/src/ap-apollo-integration-example/scheduling-service/app/graphql/types/base_field.rb:13:in `resolve_field'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/schema/member/instrumentation.rb:80:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/schema/member/instrumentation.rb:80:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/field.rb:248:in `resolve'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/execute.rb:321:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/schema/middleware_chain.rb:49:in `invoke_core'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/schema/middleware_chain.rb:38:in `invoke'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/execute.rb:129:in `block in resolve_field'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/tracing.rb:62:in `block in trace'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/tracing.rb:76:in `call_tracers'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/tracing.rb:62:in `trace'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/execute.rb:128:in `resolve_field'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/execute.rb:92:in `block in resolve_selection'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/execute.rb:85:in `each'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/execute.rb:85:in `resolve_selection'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/execute.rb:56:in `block in resolve_root_selection'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/tracing.rb:62:in `block in trace'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/tracing.rb:76:in `call_tracers'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/tracing.rb:62:in `trace'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/execute.rb:49:in `resolve_root_selection'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/execute.rb:31:in `begin_query'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/multiplex.rb:112:in `begin_query'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/multiplex.rb:84:in `block in run_as_multiplex'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/multiplex.rb:83:in `map'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/multiplex.rb:83:in `run_as_multiplex'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/multiplex.rb:62:in `block (2 levels) in run_queries'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/multiplex.rb:186:in `block in instrument_and_analyze'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/instrumentation.rb:29:in `block (2 levels) in apply_instrumenters'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/instrumentation.rb:46:in `block (2 levels) in each_query_call_hooks'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/instrumentation.rb:41:in `each_query_call_hooks'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/instrumentation.rb:45:in `block in each_query_call_hooks'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/instrumentation.rb:72:in `call_hooks'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/instrumentation.rb:44:in `each_query_call_hooks'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/instrumentation.rb:27:in `block in apply_instrumenters'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/instrumentation.rb:72:in `call_hooks'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/instrumentation.rb:26:in `apply_instrumenters'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/multiplex.rb:174:in `instrument_and_analyze'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/multiplex.rb:61:in `block in run_queries'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/tracing.rb:62:in `block in trace'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/tracing.rb:76:in `call_tracers'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/tracing.rb:62:in `trace'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/multiplex.rb:59:in `run_queries'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/execution/multiplex.rb:49:in `run_all'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/schema.rb:392:in `block in multiplex'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/schema.rb:1279:in `with_definition_error_check'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/schema.rb:391:in `multiplex'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/graphql-1.9.12/lib/graphql/schema.rb:368:in `execute'",
"/Users/ryanme/src/ap-apollo-integration-example/scheduling-service/app/controllers/graphql_controller.rb:10:in `execute'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/abstract_controller/base.rb:196:in `process_action'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_controller/metal/rendering.rb:30:in `process_action'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/abstract_controller/callbacks.rb:42:in `block in process_action'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/activesupport-6.0.0/lib/active_support/callbacks.rb:135:in `run_callbacks'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/abstract_controller/callbacks.rb:41:in `process_action'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_controller/metal/rescue.rb:22:in `process_action'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_controller/metal/instrumentation.rb:33:in `block in process_action'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/activesupport-6.0.0/lib/active_support/notifications.rb:180:in `block in instrument'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/activesupport-6.0.0/lib/active_support/notifications/instrumenter.rb:24:in `instrument'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/activesupport-6.0.0/lib/active_support/notifications.rb:180:in `instrument'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_controller/metal/instrumentation.rb:32:in `process_action'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_controller/metal/params_wrapper.rb:245:in `process_action'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/activerecord-6.0.0/lib/active_record/railties/controller_runtime.rb:27:in `process_action'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/abstract_controller/base.rb:136:in `process'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionview-6.0.0/lib/action_view/rendering.rb:39:in `process'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_controller/metal.rb:191:in `dispatch'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_controller/metal.rb:252:in `dispatch'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_dispatch/routing/route_set.rb:51:in `dispatch'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_dispatch/routing/route_set.rb:33:in `serve'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_dispatch/journey/router.rb:49:in `block in serve'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_dispatch/journey/router.rb:32:in `each'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_dispatch/journey/router.rb:32:in `serve'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_dispatch/routing/route_set.rb:837:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/rack-2.0.7/lib/rack/tempfile_reaper.rb:15:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/rack-2.0.7/lib/rack/etag.rb:25:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/rack-2.0.7/lib/rack/conditional_get.rb:38:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/rack-2.0.7/lib/rack/head.rb:12:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_dispatch/http/content_security_policy.rb:18:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/rack-2.0.7/lib/rack/session/abstract/id.rb:232:in `context'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/rack-2.0.7/lib/rack/session/abstract/id.rb:226:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_dispatch/middleware/cookies.rb:648:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/activerecord-6.0.0/lib/active_record/migration.rb:567:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_dispatch/middleware/callbacks.rb:27:in `block in call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/activesupport-6.0.0/lib/active_support/callbacks.rb:101:in `run_callbacks'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_dispatch/middleware/callbacks.rb:26:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_dispatch/middleware/executor.rb:14:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_dispatch/middleware/actionable_exceptions.rb:17:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_dispatch/middleware/debug_exceptions.rb:32:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/web-console-4.0.1/lib/web_console/middleware.rb:132:in `call_app'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/web-console-4.0.1/lib/web_console/middleware.rb:28:in `block in call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/web-console-4.0.1/lib/web_console/middleware.rb:17:in `catch'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/web-console-4.0.1/lib/web_console/middleware.rb:17:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/railties-6.0.0/lib/rails/rack/logger.rb:38:in `call_app'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/railties-6.0.0/lib/rails/rack/logger.rb:26:in `block in call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/activesupport-6.0.0/lib/active_support/tagged_logging.rb:80:in `block in tagged'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/activesupport-6.0.0/lib/active_support/tagged_logging.rb:28:in `tagged'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/activesupport-6.0.0/lib/active_support/tagged_logging.rb:80:in `tagged'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/railties-6.0.0/lib/rails/rack/logger.rb:26:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/sprockets-rails-3.2.1/lib/sprockets/rails/quiet_assets.rb:13:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_dispatch/middleware/remote_ip.rb:81:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_dispatch/middleware/request_id.rb:27:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/rack-2.0.7/lib/rack/method_override.rb:22:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/rack-2.0.7/lib/rack/runtime.rb:22:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/activesupport-6.0.0/lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_dispatch/middleware/executor.rb:14:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_dispatch/middleware/static.rb:126:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/rack-2.0.7/lib/rack/sendfile.rb:111:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/actionpack-6.0.0/lib/action_dispatch/middleware/host_authorization.rb:83:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/webpacker-4.0.7/lib/webpacker/dev_server_proxy.rb:29:in `perform_request'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/rack-proxy-0.6.5/lib/rack/proxy.rb:57:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/railties-6.0.0/lib/rails/engine.rb:526:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/puma-3.12.1/lib/puma/configuration.rb:227:in `call'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/puma-3.12.1/lib/puma/server.rb:660:in `handle_request'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/puma-3.12.1/lib/puma/server.rb:474:in `process_client'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/puma-3.12.1/lib/puma/server.rb:334:in `block in run'",
"/Users/ryanme/.rvm/gems/ruby-2.5.0/gems/puma-3.12.1/lib/puma/thread_pool.rb:135:in `block in spawn_thread'"
]
},
"data": {}
}
},
"exception": {
"stacktrace": [
"Error: 500: Internal Server Error",
" at RemoteGraphQLDataSource.<anonymous> (/Users/ryanme/src/ap-apollo-integration-example/gateway/node_modules/@apollo/gateway/dist/datasources/RemoteGraphQLDataSource.js:94:25)",
" at Generator.next (<anonymous>)",
" at /Users/ryanme/src/ap-apollo-integration-example/gateway/node_modules/@apollo/gateway/dist/datasources/RemoteGraphQLDataSource.js:7:71",
" at new Promise (<anonymous>)",
" at __awaiter (/Users/ryanme/src/ap-apollo-integration-example/gateway/node_modules/@apollo/gateway/dist/datasources/RemoteGraphQLDataSource.js:3:12)",
" at RemoteGraphQLDataSource.errorFromResponse (/Users/ryanme/src/ap-apollo-integration-example/gateway/node_modules/@apollo/gateway/dist/datasources/RemoteGraphQLDataSource.js:84:16)",
" at RemoteGraphQLDataSource.<anonymous> (/Users/ryanme/src/ap-apollo-integration-example/gateway/node_modules/@apollo/gateway/dist/datasources/RemoteGraphQLDataSource.js:67:34)",
" at Generator.next (<anonymous>)",
" at /Users/ryanme/src/ap-apollo-integration-example/gateway/node_modules/@apollo/gateway/dist/datasources/RemoteGraphQLDataSource.js:7:71",
" at new Promise (<anonymous>)"
]
}
}
}
],
"data": {
"customers": [
{
"id": "1",
"name": "Ryan",
"schedules": null
},
{
"id": "2",
"name": "Jackie",
"schedules": null
}
],
"schedules": [
{
"id": "1",
"time": "tomorrow",
"customer": {
"id": "1",
"name": "Ryan",
"schedules": [
{
"id": "1"
},
{
"id": "2"
}
]
}
},
{
"id": "2",
"time": "now",
"customer": {
"id": "2",
"name": "Jackie",
"schedules": [
{
"id": "1"
},
{
"id": "2"
}
]
}
}
]
}
}
I managed to replicate the error just on the scheduling service with the following query
{
_entities(representations: [{__typename: "Customer", id: "1"}, {__typename:"Customer", id: "2"}]) {
__typename
}
}
I am running rails 6 with the latest version of graphql-ruby and apollo-federation-ruby.
# types/customer_type.rb
module Types
class CustomerType < Types::BaseObject
extend_type
key fields: 'id'
field :id, ID, null: false, external: true
field :schedules, [ScheduleType], null: true
# I simplified this method temporarily while testing
def schedules
[
{
id: 1,
customer_id: 1,
time: "today"
},
{
id: 2,
customer_id: 1,
time: "tommorrow"
}
]
end
end
end
# types/customer_type.rb
module Types
class ScheduleType < Types::BaseObject
key fields: 'id'
field :id, ID, null: false
field :time, String, null: false
field :customer, Types::CustomerType, null: true
def self.resolve_reference(reference, context)
Schedule.find { |schedule| schedule[:id] == reference[:id] }
end
def customer
{ __typename: 'Customer', id: object[:customer_id] }
end
end
end
# schema
class SchedulingServiceSchema < GraphQL::Schema
include ApolloFederation::Schema
mutation(Types::MutationType)
query(Types::QueryType)
orphan_types Types::CustomerType
end
module Types
class QueryType < Types::BaseObject
# Add root-level fields here.
# They will be entry points for queries on your schema.
# TODO: remove me
field :test_field, String, null: false,
description: "An example field added by the generator"
def test_field
"Hello World!"
end
field :schedules, [ScheduleType], null: false
def schedules
Schedule.all
end
end
end
I have 4 other services written in nodejs that are connected to my data gateway and running fine, just trying to get the ruby one to work. Any help would be appreciated.
While implementing the federation spec for a few types, I got tripped up when referencing fields in the key
helper and resolve_reference
. Since all of the fields in graphql-ruby
use snake case, I sometimes forget that the helpers for federation use the actual camelCased field names.
When authoring this library, was snake_case considered, and if so were there any reasons to not use it? I feel as though the mix of snake & camel case may be slightly confusing, what are your thoughts?
In the docs you have the following
class BaseObject < GraphQL::Schema::Object
include ApolloFederation::Object
field_class BaseField # what is this for?
end
What is the purpose of the second last line?
The error is unknown keywords: request, current_user, auth, __pro_access_strategy__
Reported from:
/gem_bag/ruby/2.5.0/gems/apollo-federation-1.0.1/lib/apollo-federation/schema.rb:50:in `federation_sdl'
/gem_bag/ruby/2.5.0/gems/apollo-federation-1.0.1/lib/apollo-federation/service_field.rb:13:in `_service'
The query is:
query GetServiceDefinition { _service { sdl } }
Which is fired automatically from @apollo/gateway
.
Fairly certain it is from #45.
Hey Gusto,
Thanks for the awesome gem. What is the process to get involved with maintaining the gem? A few things that I think i could help with
Thanks!
Tracing works differently in a federated system than it does for a standard GraphQL server. It's officially documented here.
Question 1: do you think this library should include support for generating the trace proto and attaching it to the response? It could be a separately library, but I think it makes sense to include all federation concerns in one place.
Question 2: is anyone working on this?
The node.js implementation is here:
There's some unfinished ruby work for standard single-server traces (that are sent directly to Apollo's traces ingress endpoint instead of attached to the response to the gateway) here.
Hi ๐
We are working on building a federated graph and this gem is helping us getting closer to a production release. ๐ I've been tasked to control the visibility of the schema so the external Federated schema does not expose every single field from internal schemas.
So far my proposed solution is simple, users opt-in to have their fields/objects exposed, I am following the documentation on Limiting Visibility and Extending the GraphQL-Ruby Type Definition System and this is what I come up with:
class BaseField < GraphQL::Schema::Field
include ApolloFederation::Field
argument_class BaseArgument
def initialize(*_args, expose: false, **_kwargs)
@exposed = exposed
super(*args, **kwargs, &block)
end
def to_graphql
field_definition = super
field_definition.metadata[:expose] = @expose
field_definition
end
end
# ---
class Query < BaseObject
field :foobar, Foobar, null: true, expose: true
end
# ---
# controller
result = GraphSchema.execute(query, variables: variables, operation_name: operation_name, context: context, only: ExposeWhitelist.new)
# ---
# Work in progress
class ExposeWhitelist
def call(schema_member, _context)
if schema_member.is_a?(GraphQL::Field)
# TODO: better detection of Federated fields.
schema_member.name == "_entities" ||
schema_member.name == "_service" ||
schema_member.name == "sdl" ||
schema_member.introspection? ||
schema_member.metadata[:exposed]
elsif schema_member.is_a?(GraphQL::Argument)
# TODO: implement logic here to decide either or not Argument should be exposed.
true
else
# TODO: implement logic here to decide either or not Type, Enum should be exposed.
true
end
end
end
This causes the following error:
{
"errors": [
{
"message": "A copy of Types has been removed from the module tree but is still active!",
"extensions": {
"type": "ArgumentError",
It seems that the challenge lies around the fact that the module ApolloFederation::Field
also defines an initialize
method and the super
calls gets confused ๐ค and crashes in unexpected ways, the bare minimum example I tried just overwrote initialize
while including the Federation module and that was enough to cause issues.
Based of the following technique for extending a method from a module, I came up with the following solution:
module ApolloFederation
module Field
include HasDirectives
def self.included(base)
base.extend ClassMethods
base.overwrite_initialize
base.instance_eval do
def method_added(name)
return if name != :initialize
overwrite_initialize
end
end
end
module ClassMethods
def overwrite_initialize
class_eval do
unless method_defined?(:apollo_federation_initialize)
define_method(:apollo_federation_initialize) do |*args, external: false, requires: nil, provides: nil, **kwargs, &block|
if external
add_directive(name: 'external')
end
if requires
add_directive(
name: 'requires',
arguments: [
name: 'fields',
values: requires[:fields],
],
)
end
if provides
add_directive(
name: 'provides',
arguments: [
name: 'fields',
values: provides[:fields],
],
)
end
original_initialize(*args, **kwargs, &block)
end
end
if instance_method(:initialize) != instance_method(:apollo_federation_initialize)
alias_method :original_initialize, :initialize
alias_method :initialize, :apollo_federation_initialize)
end
end
end
end
end
end
Thoughts on this approach, would you consider a Pull Request with similar change?
Thanks for open sourcing this Apollo Federation solution, I appreciate it ๐
In rmosolgo/graphql-ruby#2246, any key in the extensions hash is stringified during execution for consistency.
Hence in places like https://github.com/Gusto/apollo-federation-ruby/blob/master/lib/apollo-federation/tracing.rb#L39, it's probably better to use strings to avoid subtle bugs.
Has anyone had any success building and running federated services and a gateway in a Rails app? I already have federated my schema and am trying to get each GraphQLServer running for each service.
GraphQL Ruby has its own native batch loading abstraction that uses Ruby's fibers.
Right now, we are using it to resolve all N+1 queries in our app, except for incoming top level subgraph queries which are resolved by the Query._entities
field.
Given a query:
query($representations:[_Any!]!){
_entities(representations:$representations){
...on BlogPost{
id
title
}
}
}
Where we have variables representing two objects to be resolved:
{
"variables": {
"representations": [
{
"__typename": "BlogPost",
"id": "1"
},
{
"__typename": "BlogPost",
"id": "2"
}
]
}
}
One might write a simple BlogPostType.resolve_reference
method that looks like this:
def self.resolve_reference(reference, context)
post = context.dataloader.with(DataSources::ActiveRecord, BlogPost).load(reference[:id])
return nil unless post
BlogPostService.new(blog_post: post)
end
However, this doesn't work with Dataloader
. With debugging statements around it, The moment #load
is called, we also end up instantiating theDataSource
and call #fetch
on it. This ends up generating the N+1s as before.
In Apollo's own implementation, the typical recommendation is to do batch loading in __resolveReference
. We can differ in implementation on Ruby, but if we're sticking to the same paradigm, I would hope that there is a way for #resolve_reference
to work with the native batch loader.
Still investigating, will update this section of the issue once I have more information.
As a starting point, this is the resolver for Query._entities
. It uses a map
of calls to each type's resolve_reference
, which probably does not work with the Dataloader.
apollo-federation-ruby/lib/apollo-federation/entities_field.rb
Lines 29 to 55 in 1d3baf4
Using the new graphql-ruby
interpreter with apollo-federation-ruby
does not work.
{ _service { sdl } }
Field '_service' doesn't exist on type 'Query'
class AppSchema < GraphQL::Schema
include ApolloFederation::Schema
use GraphQL::Execution::Interpreter
use GraphQL::Analysis::AST
# ...
end
graphql-ruby
: 1.10.7
apollo-federation-ruby
: 1.0.4
Note: The new interpreter + apollo-federation-ruby
did work with graphql-ruby
1.9.19
.
I've configured my Rails app per documentation and now trying to get the schema through my gateway build in Node.js, but got:
{ message: 'Field \'_service\' doesn\'t exist on type \'Query\'',
locations: [ { line: 1, column: 30 } ],
path: [ 'query GetServiceDefinition', '_service' ],
extensions:
{ code: 'undefinedField',
typeName: 'Query',
fieldName: '_service' } } 0 [ { message: 'Field \'_service\' doesn\'t exist on type \'Query\'',
locations: [ [Object] ],
path: [ 'query GetServiceDefinition', '_service' ],
extensions:
{ code: 'undefinedField',
typeName: 'Query',
fieldName: '_service' } } ]
The gateway is working fine with other federated apps, either build in rails or Node.js.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.