{"id":13879699,"url":"https://github.com/Gusto/apollo-federation-ruby","last_synced_at":"2025-07-16T15:32:46.238Z","repository":{"id":39879814,"uuid":"192986546","full_name":"Gusto/apollo-federation-ruby","owner":"Gusto","description":"A Ruby implementation of Apollo Federation","archived":false,"fork":false,"pushed_at":"2024-04-02T21:15:11.000Z","size":1848,"stargazers_count":215,"open_issues_count":26,"forks_count":65,"subscribers_count":19,"default_branch":"main","last_synced_at":"2024-04-14T07:25:51.164Z","etag":null,"topics":["apollographql","graphql","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Gusto.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-06-20T20:57:37.000Z","updated_at":"2024-06-02T21:27:08.067Z","dependencies_parsed_at":"2024-06-02T21:26:35.895Z","dependency_job_id":"e885c051-57f4-4f4d-8490-f4aca4d383d7","html_url":"https://github.com/Gusto/apollo-federation-ruby","commit_stats":{"total_commits":204,"total_committers":28,"mean_commits":7.285714285714286,"dds":0.7696078431372549,"last_synced_commit":"102a63bba78720e5c2ae5b59f1a7e6f984aef047"},"previous_names":[],"tags_count":61,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gusto%2Fapollo-federation-ruby","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gusto%2Fapollo-federation-ruby/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gusto%2Fapollo-federation-ruby/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gusto%2Fapollo-federation-ruby/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Gusto","download_url":"https://codeload.github.com/Gusto/apollo-federation-ruby/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226143895,"owners_count":17580245,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apollographql","graphql","ruby"],"created_at":"2024-08-06T08:02:29.479Z","updated_at":"2025-07-16T15:32:46.229Z","avatar_url":"https://github.com/Gusto.png","language":"Ruby","readme":"# apollo-federation\n\n[![CircleCI](https://circleci.com/gh/Gusto/apollo-federation-ruby/tree/main.svg?style=svg)](https://circleci.com/gh/Gusto/apollo-federation-ruby/tree/main)\n\nThis gem extends the [GraphQL Ruby](http://graphql-ruby.org/) gem to add support for creating an [Apollo Federation](https://www.apollographql.com/docs/apollo-server/federation/introduction/) schema.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'apollo-federation'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install apollo-federation\n\n## Getting Started\n\nInclude the `ApolloFederation::Argument` module in your base argument class:\n\n```ruby\nclass BaseArgument \u003c GraphQL::Schema::Argument\n  include ApolloFederation::Argument\nend\n```\n\nInclude the `ApolloFederation::Field` module in your base field class:\n\n```ruby\nclass BaseField \u003c GraphQL::Schema::Field\n  include ApolloFederation::Field\n\n  argument_class BaseArgument\nend\n```\n\nInclude the `ApolloFederation::Object` module in your base object class:\n\n```ruby\nclass BaseObject \u003c GraphQL::Schema::Object\n  include ApolloFederation::Object\n\n  field_class BaseField\nend\n```\n\nInclude the `ApolloFederation::Interface` module in your base interface module:\n\n```ruby\nmodule BaseInterface\n  include GraphQL::Schema::Interface\n  include ApolloFederation::Interface\n\n  field_class BaseField\nend\n```\n\nInclude the `ApolloFederation::Union` module in your base union class:\n\n```ruby\nclass BaseUnion \u003c GraphQL::Schema::Union\n  include ApolloFederation::Union\nend\n```\n\nInclude the `ApolloFederation::EnumValue` module in your base enum value class:\n\n```ruby\nclass BaseEnumValue \u003c GraphQL::Schema::EnumValue\n  include ApolloFederation::EnumValue\nend\n```\n\nInclude the `ApolloFederation::Enum` module in your base enum class:\n\n```ruby\nclass BaseEnum \u003c GraphQL::Schema::Enum\n  include ApolloFederation::Enum\n\n  enum_value_class BaseEnumValue\nend\n```\n\nInclude the `ApolloFederation::InputObject` module in your base input object class:\n\n```ruby\nclass BaseInputObject \u003c GraphQL::Schema::InputObject\n  include ApolloFederation::InputObject\n\n  argument_class BaseArgument\nend\n```\n\nInclude the `ApolloFederation::Scalar` module in your base scalar class:\n\n```ruby\nclass BaseScalar \u003c GraphQL::Schema::Scalar\n  include ApolloFederation::Scalar\nend\n```\n\nFinally, include the `ApolloFederation::Schema` module in your schema:\n\n```ruby\nclass MySchema \u003c GraphQL::Schema\n  include ApolloFederation::Schema\nend\n```\n\n**Optional:** To opt in to Federation v2, specify the version in your schema:\n\n```ruby\nclass MySchema \u003c GraphQL::Schema\n  include ApolloFederation::Schema\n  federation version: '2.0'\nend\n```\n\n## Example\n\nThe [`example`](./example/) folder contains a Ruby implementation of Apollo's [`federation-demo`](https://github.com/apollographql/federation-demo). To run it locally, install the Ruby dependencies:\n\n    $ bundle\n\nInstall the Node dependencies:\n\n    $ yarn\n\nStart all of the services:\n\n    $ yarn start-services\n\nStart the gateway:\n\n    $ yarn start-gateway\n\nThis will start up the gateway and serve it at http://localhost:5000.\n\n## Usage\n\nThe API is designed to mimic the API of [Apollo's federation library](https://www.apollographql.com/docs/apollo-server/federation/introduction/). It's best to read and understand the way federation works, in general, before attempting to use this library.\n\n### Extending a type\n\n[Apollo documentation](https://www.apollographql.com/docs/apollo-server/federation/core-concepts/#extending-external-types)\n\nCall `extend_type` within your class definition:\n\n```ruby\nclass User \u003c BaseObject\n  extend_type\nend\n```\n\n### The `@key` directive\n\n[Apollo documentation](https://www.apollographql.com/docs/apollo-server/federation/core-concepts/#entities-and-keys)\n\nCall `key` within your class definition:\n\n```ruby\nclass User \u003c BaseObject\n  key fields: :id\nend\n```\n\nCompound keys are also supported:\n\n```ruby\nclass User \u003c BaseObject\n  key fields: [:id, { organization: :id }]\nend\n```\n\nAs well as non-resolvable keys:\n\n```ruby\nclass User \u003c BaseObject\n  key fields: :id, resolvable: false\nend\n```\n\nSee [field set syntax](#field-set-syntax) for more details on the format of the `fields` option.\n\n### The `@external` directive\n\n[Apollo documentation](https://www.apollographql.com/docs/apollo-server/federation/core-concepts/#referencing-external-types)\n\nPass the `external: true` option to your field definition:\n\n```ruby\nclass User \u003c BaseObject\n  field :id, ID, null: false, external: true\nend\n```\n\n### The `@requires` directive\n\n[Apollo documentation](https://www.apollographql.com/docs/apollo-server/federation/advanced-features/#computed-fields)\n\nPass the `requires:` option to your field definition:\n\n```ruby\nclass Product \u003c BaseObject\n  field :price, Int, null: true, external: true\n  field :weight, Int, null: true, external: true\n  field :shipping_estimate, Int, null: true, requires: { fields: [:price, :weight] }\nend\n```\n\nSee [field set syntax](#field-set-syntax) for more details on the format of the `fields` option.\n\n### The `@provides` directive\n\n[Apollo documentation](https://www.apollographql.com/docs/apollo-server/federation/advanced-features/#using-denormalized-data)\n\nPass the `provides:` option to your field definition:\n\n```ruby\nclass Review \u003c BaseObject\n  field :author, 'User', null: true, provides: { fields: :username }\nend\n```\nSee [field set syntax](#field-set-syntax) for more details on the format of the `fields` option.\n\n### The `@shareable` directive (Apollo Federation v2)\n\n[Apollo documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives/#shareable)\n\nCall `shareable` within your class definition:\n\n```ruby\nclass User \u003c BaseObject\n  shareable\nend\n```\n\nPass the `shareable: true` option to your field definition:\n\n```ruby\nclass User \u003c BaseObject\n  field :id, ID, null: false, shareable: true\nend\n```\n\n### The `@inaccessible` directive (Apollo Federation v2)\n\n[Apollo documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives/#inaccessible)\n\nCall `inaccessible` within your class definition:\n\n```ruby\nclass User \u003c BaseObject\n  inaccessible\nend\n```\n\nPass the `inaccessible: true` option to your field definition:\n\n```ruby\nclass User \u003c BaseObject\n  field :id, ID, null: false, inaccessible: true\nend\n```\n\n### The `@override` directive (Apollo Federation v2)\n\n[Apollo documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives/#override)\n\nPass the `override:` option to your field definition:\n\n```ruby\nclass Product \u003c BaseObject\n  field :id, ID, null: false\n  field :inStock, Boolean, null: false, override: { from: 'Products' }\nend\n```\n\n### The `@tag` directive (Apollo Federation v2)\n\n[Apollo documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives/#tag)\n\nCall `tag` within your class definition:\n\n```ruby\nclass User \u003c BaseObject\n  tag name: 'private'\nend\n```\n\nPass the `tags:` option to your field definition:\n\n```ruby\nclass User \u003c BaseObject\n  field :id, ID, null: false, tags: [{ name: 'private' }]\nend\n```\n\n### Field set syntax\n\nField sets can be either strings encoded with the Apollo Field Set [syntax]((https://www.apollographql.com/docs/apollo-server/federation/federation-spec/#scalar-_fieldset)) or arrays, hashes and snake case symbols that follow the graphql-ruby conventions:\n\n```ruby\n# Equivalent to the \"organizationId\" field set\n:organization_id\n\n# Equivalent to the \"price weight\" field set\n[:price, :weight]\n\n# Equivalent to the \"id organization { id }\" field set\n[:id, { organization: :id }]\n```\n\n### Reference resolvers\n\n[Apollo documentation](https://www.apollographql.com/docs/apollo-server/api/apollo-federation/#__resolvereference)\n\nDefine a `resolve_reference` class method on your object. The method will be passed the reference from another service and the context for the query.\n\n```ruby\nclass User \u003c BaseObject\n  key fields: :user_id\n  field :user_id, ID, null: false\n  \n  def self.resolve_reference(reference, context)\n    USERS.find { |user| user[:userId] == reference[:userId] }\n  end\nend\n```\n\nTo maintain backwards compatibility, by default, reference hash keys are camelcase. They can be underscored by setting `underscore_reference_keys` on your entity class. In order to maintain consistency with GraphQL Ruby, we may change the keys to be underscored by default in a future major release.\n\n```ruby\nclass User \u003c BaseObject\n  key fields: :user_id\n  field :user_id, ID, null: false\n  underscore_reference_keys true\n  \n  def self.resolve_reference(reference, context)\n    USERS.find { |user| user[:user_id] == reference[:user_id] }\n  end\nend\n```\nAlternatively you can change the default for your project by setting `underscore_reference_keys` on `BaseObject`:\n\n```ruby\nclass BaseObject \u003c GraphQL::Schema::Object\n  include ApolloFederation::Object\n\n  field_class BaseField\n  underscore_reference_keys true\nend\n```\n\n### Tracing\n\nTo support [federated tracing](https://www.apollographql.com/docs/apollo-server/federation/metrics/):\n\n1. Add `trace_with ApolloFederation::Tracing::Tracer` to your schema class.\n2. Change your controller to add `tracing_enabled: true` to the execution context based on the presence of the \"include trace\" header:\n   ```ruby\n   def execute\n     # ...\n     context = {\n       # Pass in the headers from your web framework. For Rails this will be request.headers\n       # but for other frameworks you can pass the Rack env.\n       tracing_enabled: ApolloFederation::Tracing.should_add_traces(request.headers)\n     }\n     # ...\n   end\n   ```\n\n## Exporting the Federated SDL\n\nWhen using tools like [rover](https://www.apollographql.com/docs/rover/) for schema validation, etc., add a Rake task that prints the Federated SDL to a file:\n\n```rb\nnamespace :graphql do\n  namespace :federation do\n    task :dump do\n      File.write(\"schema.graphql\", MySchema.federation_sdl)\n    end\n  end\nend\n```\n\nExample validation check with Rover and Apollo Studio:\n\n```sh\nbin/rake graphql:federation:dump\nrover subgraph check mygraph@current --name mysubgraph --schema schema.graphql\n```\n\n## Testing the federated schema\n\nThis library does not include any testing helpers currently. A federated service receives subgraph queries from the Apollo Gateway via the `_entities` field and that can be tested in a request spec.\n\nWith Apollo Gateway setup to hit your service locally or by using existing query logs, you can retrieve the generated `_entities` queries.\n\nFor example, if you have a blog service that exposes posts by a given author, the query received by the service might look like this.\n\n```graphql\nquery($representations: [_Any!]!) {\n  _entities(representations: $representations) {\n    ... on BlogPost {\n      id\n      title\n      body\n    }\n  }\n}\n```\n\nWhere `$representations` is an array of entity references from the gateway.\n\n```JSON\n{\n  \"representations\": [\n    {\n      \"__typename\": \"BlogPost\",\n      \"id\": 1\n    },\n    {\n      \"__typename\": \"BlogPost\",\n      \"id\": 2\n    }\n  ]\n}\n```\n\nUsing RSpec as an example, a request spec for this query.\n\n```ruby\nit \"resolves the blog post entities\" do\n  blog_post = BlogPost.create!(attributes)\n\n  query = \u003c\u003c~GRAPHQL\n    query($representations: [_Any!]!) {\n      _entities(representations: $representations) {\n        ... on BlogPost {\n          id\n          title\n          body\n        }\n      }\n    }\n  GRAPHQL\n\n  variables = { representations: [{ __typename: \"BlogPost\", id: blog_post.id }] }\n\n  result = Schema.execute(query, variables: variables)\n\n  expect(result.dig(\"data\", \"_entities\", 0, \"id\")).to eq(blog_post.id)\nend\n```\n\nSee discussion at [#74](https://github.com/Gusto/apollo-federation-ruby/issues/74) and an [internal spec that resolves \\_entities](https://github.com/Gusto/apollo-federation-ruby/blob/1d3baf4f8efcd02e7bf5bc7e3fee5b4fb963cd25/spec/apollo-federation/entities_field_spec.rb#L164) for more details.\n\n## Known Issues and Limitations\n\n- For GraphQL older than 1.12, the interpreter runtime has to be used.\n- Does not add directives to the output of `Schema.to_definition`. Since `graphql-ruby` doesn't natively support schema directives, the\n  directives will only be visible to the [Apollo Gateway](https://www.apollographql.com/docs/apollo-server/api/apollo-gateway/) through the `Query._service` field (see the [Apollo Federation specification](https://www.apollographql.com/docs/apollo-server/federation/federation-spec/)) or via [`Schema#federation_sdl`](https://github.com/Gusto/apollo-federation-ruby/blob/1d3baf4f8efcd02e7bf5bc7e3fee5b4fb963cd25/lib/apollo-federation/schema.rb#L19) as explained above.\n\n## Maintainers\n\nGusto GraphQL Team:\n\n- [Sara Laupp](https://github.com/slauppy)\n- [Seth Copeland](https://github.com/sethc2)\n- [Simon Coffin](https://github.com/simoncoffin)\n- [Sofia Carrillo](https://github.com/sofie-c)\n","funding_links":[],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGusto%2Fapollo-federation-ruby","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FGusto%2Fapollo-federation-ruby","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGusto%2Fapollo-federation-ruby/lists"}