{"id":15065855,"url":"https://github.com/graphql-python/graphene-federation","last_synced_at":"2025-09-08T12:32:07.572Z","repository":{"id":65576857,"uuid":"533231798","full_name":"graphql-python/graphene-federation","owner":"graphql-python","description":"Federation implementation for Graphene. ","archived":false,"fork":false,"pushed_at":"2024-06-15T09:00:24.000Z","size":335,"stargazers_count":40,"open_issues_count":9,"forks_count":10,"subscribers_count":9,"default_branch":"main","last_synced_at":"2024-12-26T23:06:42.012Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","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/graphql-python.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","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":"2022-09-06T08:31:50.000Z","updated_at":"2024-06-14T13:15:40.000Z","dependencies_parsed_at":"2024-06-19T05:22:55.999Z","dependency_job_id":"643ba5af-870b-4c92-ac60-c33873fa3262","html_url":"https://github.com/graphql-python/graphene-federation","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphql-python%2Fgraphene-federation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphql-python%2Fgraphene-federation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphql-python%2Fgraphene-federation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphql-python%2Fgraphene-federation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/graphql-python","download_url":"https://codeload.github.com/graphql-python/graphene-federation/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":232308556,"owners_count":18503119,"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":[],"created_at":"2024-09-25T00:55:44.585Z","updated_at":"2025-01-03T08:12:13.352Z","avatar_url":"https://github.com/graphql-python.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# graphene-federation\n\nFederation support for ![Graphene Logo](http://graphene-python.org/favicon.png) [Graphene](http://graphene-python.org) following the [Apollo Federation specifications](https://www.apollographql.com/docs/federation/subgraph-spec).\n\n[![PyPI version][pypi-image]][pypi-url]\n[![Unit Tests Status][unit-tests-image]][unit-tests-url]\n[![Coverage Status][coveralls-image]][coveralls-url]\n[![Integration Tests Status][integration-tests-image]][integration-tests-url]\n\n[pypi-image]: https://badge.fury.io/py/graphene-federation.svg\n[pypi-url]: https://pypi.org/project/graphene-federation/\n[unit-tests-image]: https://github.com/graphql-python/graphene-federation/workflows/Unit%20Tests/badge.svg?branch=main\n[unit-tests-url]: https://github.com/graphql-python/graphene-federation/actions?query=workflow%3A\"Unit+Tests\"\n[coveralls-image]: https://coveralls.io/repos/github/graphql-python/graphene-federation/badge.svg?branch=main\n[coveralls-url]: https://coveralls.io/github/graphql-python/graphene-federation?branch=main\n[integration-tests-image]: https://github.com/graphql-python/graphene-federation/workflows/Integration%20Tests/badge.svg?branch=main\n[integration-tests-url]: https://github.com/graphql-python/graphene-federation/actions?query=workflow%3A\"Integration+Tests\"\n\n\nThis repository is heavily based on the repo it was forked from... Huge thanks to [Preply for setting up the foundations](https://medium.com/preply-engineering/apollo-federation-support-in-graphene-761a0512456d).\n\n\nWARNING: This version is not compatible with `graphene` version below v3.\nIf you need to use a version compatible with `graphene` v2 I recommend using the version 1.0.0 of `graphene_federation`.\n\n------------------------\n\n## Supported Features\n\n* `sdl` (`_service` on field): enable to add schema in federation (as is)\n\n## Apollo Spec Supported\n\n- [x] v1.0\n- [x] v2.0\n- [x] v2.1\n- [x] v2.2\n- [x] v2.3\n- [x] v2.4\n- [x] v2.5 \n- [x] v2.6 `STABLE_VERSION` . Rover dev supports only upto v2.6\n- [x] v2.7 `LATEST_VERSION`\n\nAll directives could be easily integrated with the help of [graphene-directives](https://github.com/strollby/graphene-directives). \nNow every directive's values are validated at run time itself by [graphene-directives](https://github.com/strollby/graphene-directives).\n\n### Directives (v2.7)\n\n```graphql\ndirective @composeDirective(name: String!) repeatable on SCHEMA\ndirective @extends on OBJECT | INTERFACE\ndirective @external on OBJECT | FIELD_DEFINITION\ndirective @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE\ndirective @inaccessible on\n  | FIELD_DEFINITION\n  | OBJECT\n  | INTERFACE\n  | UNION\n  | ENUM\n  | ENUM_VALUE\n  | SCALAR\n  | INPUT_OBJECT\n  | INPUT_FIELD_DEFINITION\n  | ARGUMENT_DEFINITION\ndirective @interfaceObject on OBJECT\ndirective @override(from: String!, label: String) on FIELD_DEFINITION\ndirective @provides(fields: FieldSet!) on FIELD_DEFINITION\ndirective @requires(fields: FieldSet!) on FIELD_DEFINITION\ndirective @shareable repeatable on FIELD_DEFINITION | OBJECT\ndirective @tag(name: String!) repeatable on\n  | FIELD_DEFINITION\n  | INTERFACE\n  | OBJECT\n  | UNION\n  | ARGUMENT_DEFINITION\n  | SCALAR\n  | ENUM\n  | ENUM_VALUE\n  | INPUT_OBJECT\n  | INPUT_FIELD_DEFINITION\ndirective @authenticated on\n    FIELD_DEFINITION\n  | OBJECT\n  | INTERFACE\n  | SCALAR\n  | ENUM\ndirective @requiresScopes(scopes: [[federation__Scope!]!]!) on\n    FIELD_DEFINITION\n  | OBJECT\n  | INTERFACE\n  | SCALAR\n  | ENUM\ndirective @policy(policies: [[federation__Policy!]!]!) on\n  | FIELD_DEFINITION\n  | OBJECT\n  | INTERFACE\n  | SCALAR\n  | ENUM\nscalar federation__Policy\nscalar federation__Scope\nscalar FieldSet\n```\n\nRead about directives in [official documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives)\n\n\nEach type which is decorated with `@key` or `@extends` is added to the `_Entity` union.\nThe [`__resolve_reference` method](https://www.apollographql.com/docs/federation/api/apollo-federation/#__resolvereference) can be defined for each type that is an entity.\nNote that since the notation with double underscores can be problematic in Python for model inheritance this resolver method can also be named `_resolve_reference` (the `__resolve_reference` method will take precedence if both are declared).\n\nThis method is called whenever an entity is requested as part of the fulfilling a query plan.\nIf not explicitly defined, the default resolver is used.\nThe default resolver just creates instance of type with passed fieldset as kwargs, see [`entity.get_entity_query`](graphene_federation/entity.py) for more details\n* You should define `__resolve_reference`, if you need to extract object before passing it to fields resolvers (example: [FileNode](integration_tests/service_b/src/schema.py))\n* You should not define `__resolve_reference`, if fields resolvers need only data passed in fieldset (example: [FunnyText](integration_tests/service_a/src/schema.py))\nRead more in [official documentation](https://www.apollographql.com/docs/apollo-server/api/apollo-federation/#__resolvereference).\n\n------------------------\n\n## Example\n\nHere is an example of implementation based on the [Apollo Federation introduction example](https://www.apollographql.com/docs/federation/).\nIt implements a federation schema for a basic e-commerce application over three services: accounts, products, reviews.\n\n### Accounts\nFirst add an account service that expose a `User` type that can then be referenced in other services by its `id` field:\n\n```python\nfrom graphene import Field, Int, ObjectType, String\n\nfrom graphene_federation import LATEST_VERSION, build_schema, key\n\n\n@key(\"id\")\nclass User(ObjectType):\n    id = Int(required=True)\n    username = String(required=True)\n\n    def __resolve_reference(self, info, **kwargs):\n        \"\"\"\n        Here we resolve the reference of the user entity referenced by its `id` field.\n        \"\"\"\n        return User(id=self.id, email=f\"user_{self.id}@mail.com\")\n\n\nclass Query(ObjectType):\n    me = Field(User)\n\n\nschema = build_schema(query=Query, federation_version=LATEST_VERSION)\n```\n\n### Product\nThe product service exposes a `Product` type that can be used by other services via the `upc` field:\n\n```python\nfrom graphene import Argument, Int, List, ObjectType, String\n\nfrom graphene_federation import LATEST_VERSION, build_schema, key\n\n\n@key(\"upc\")\nclass Product(ObjectType):\n    upc = String(required=True)\n    name = String(required=True)\n    price = Int()\n\n    def __resolve_reference(self, info, **kwargs):\n        \"\"\"\n        Here we resolve the reference of the product entity referenced by its `upc` field.\n        \"\"\"\n        return Product(upc=self.upc, name=f\"product {self.upc}\")\n\n\nclass Query(ObjectType):\n    topProducts = List(Product, first=Argument(Int, default_value=5))\n\n\nschema = build_schema(query=Query, federation_version=LATEST_VERSION)\n```\n\n### Reviews\nThe reviews service exposes a `Review` type which has a link to both the `User` and `Product` types.\nIt also has the ability to provide the username of the `User`.\nOn top of that it adds to the `User`/`Product` types (that are both defined in other services) the ability to get their reviews.\n\n```python\nfrom graphene import Field, Int, List, ObjectType, String\n\nfrom graphene_federation import LATEST_VERSION, build_schema, external, key, provides\n\n\n@key(\"id\")\nclass User(ObjectType):\n    id = external(Int(required=True))\n    reviews = List(lambda: Review)\n\n    def resolve_reviews(self, info, *args, **kwargs):\n        \"\"\"\n        Get all the reviews of a given user. (not implemented here)\n        \"\"\"\n        return []\n\n\n@key(\"upc\")\nclass Product(ObjectType):\n    upc = external(String(required=True))\n    reviews = List(lambda: Review)\n\n\nclass Review(ObjectType):\n    body = String()\n    author = provides(Field(User), fields=\"username\")\n    product = Field(Product)\n\n\nclass Query(ObjectType):\n    review = Field(Review)\n\n\nschema = build_schema(query=Query, federation_version=LATEST_VERSION)\n```\n\n### Federation\n\nNote that each schema declaration for the services is a valid graphql schema (it only adds the `_Entity` and `_Service` types).\nThe best way to check that the decorator are set correctly is to request the service sdl:\n\n```python\nfrom graphql import graphql\n\nquery = \"\"\"\nquery {\n    _service {\n        sdl\n    }\n}\n\"\"\"\n\nresult = graphql(schema, query)\nprint(result.data[\"_service\"][\"sdl\"])\n```\n\nThose can then be used in a federated schema.\n\nYou can find more examples in the unit / integration tests and [examples folder](examples/).\n\nThere is also a cool [example](https://github.com/preply/graphene-federation/issues/1) of integration with Mongoengine.\n\n------------------------\n## Other Notes\n\n### build_schema new arguments\n\n- `schema_directives` (`Collection[SchemaDirective]`): Directives that can be defined at `DIRECTIVE_LOCATION.SCHEMA` with their argument values.\n- `include_graphql_spec_directives` (`bool`): Includes directives defined by GraphQL spec (`@include`, `@skip`, `@deprecated`, `@specifiedBy`)\n- `federation_version` (`FederationVersion`): Specify the version explicit (default STABLE_VERSION)\n\n### Directives Additional arguments\n\n-  `federation_version`: (`FederationVersion` = `LATEST_VERSION`) : You can use this to take a directive from a particular federation version\n\nNote: The `federation_version` in `build_schema` is given higher priority. If the directive you have chosen is not compatible, it will raise an error\n\n### Custom Directives\n\nYou can define custom directives as follows\n\n```python\nfrom graphene import Field, ObjectType, String\nfrom graphql import GraphQLArgument, GraphQLInt, GraphQLNonNull\n\nfrom graphene_federation import ComposableDirective, DirectiveLocation, LATEST_VERSION\nfrom graphene_federation import build_schema\n\nCacheDirective = ComposableDirective(\n    name=\"cache\",\n    locations=[DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.OBJECT],\n    args={\n        \"maxAge\": GraphQLArgument(\n            GraphQLNonNull(GraphQLInt), description=\"Specifies the maximum age for cache in seconds.\"\n        ),\n    },\n    description=\"Caching directive to control cache behavior.\",\n    spec_url=\"https://specs.example.dev/directives/v1.0\",\n)\n\ncache = CacheDirective.decorator()\n\n\n@cache(max_age=20)\nclass Review(ObjectType):\n    body = cache(field=String(), max_age=100)\n\n\nclass Query(ObjectType):\n    review = Field(Review)\n\n\nschema = build_schema(\n    query=Query,\n    directives=(CacheDirective,),\n    federation_version=LATEST_VERSION ,\n)\n```\n\nThis will automatically add @link and @composeDirective to schema\n\n\n```graphql\nextend schema\n\t@link(url: \"https://specs.apollo.dev/federation/v2.6\", import: [\"@composeDirective\"])\n\t@link(url: \"https://specs.example.dev/directives/v1.0\", import: [\"@cache\"])\n\t@composeDirective(name: \"@cache\")\n\n\"\"\"Caching directive to control cache behavior.\"\"\"\ndirective @cache(\n  \"\"\"Specifies the maximum age for cache in seconds.\"\"\"\n  maxAge: Int!\n) on FIELD_DEFINITION | OBJECT\n\ntype Query {\n  review: Review\n  _service: _Service!\n}\n\ntype Review  @cache(maxAge: 20) {\n  body: String @cache(maxAge: 100)\n}\n```\n\nIf you wish to add the schema_directives `@link` `@composeDirective` manually. \nYou can pass the `add_to_schema_directives` as `False`\n\n```python\nfrom graphene import Field, ObjectType, String\nfrom graphql import GraphQLArgument, GraphQLInt, GraphQLNonNull\n\nfrom graphene_federation import (ComposableDirective, DirectiveLocation, LATEST_VERSION, build_schema,\n                                 compose_directive, link_directive)\n\nCacheDirective = ComposableDirective(\n    name=\"cache\",\n    locations=[DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.OBJECT],\n    args={\n        \"maxAge\": GraphQLArgument(\n            GraphQLNonNull(GraphQLInt), description=\"Specifies the maximum age for cache in seconds.\"\n        ),\n    },\n    description=\"Caching directive to control cache behavior.\",\n    add_to_schema_directives=False\n)\n\ncache = CacheDirective.decorator()\n\n\n@cache(max_age=20)\nclass Review(ObjectType):\n    body = cache(field=String(), max_age=100)\n\n\nclass Query(ObjectType):\n    review = Field(Review)\n\n\nschema = build_schema(\n    query=Query,\n    directives=(CacheDirective,),\n    schema_directives=(\n        link_directive(url=\"https://specs.example.dev/directives/v1.0\", import_=['@cache']),\n        compose_directive(name='@cache'),\n    ),\n    federation_version=LATEST_VERSION,\n)\n```\n\n### Custom field name\n\nWhen using decorator on a field with custom name\n\n####  Case 1 (auto_camelcase=False)\n\n```python\n@key(\"identifier\")\n@key(\"validEmail\")\nclass User(ObjectType):\n    identifier = ID()\n    email = String(name=\"validEmail\")\n\nclass Query(ObjectType):\n    user = Field(User)\n\nschema = build_schema(query=Query, federation_version=LATEST_VERSION, auto_camelcase=False) # Disable auto_camelcase\n```\n\nThis works correctly.\nBy default `fields` of `@key`,`@requires` and `@provides` are not converted to camel case if `auto_camelcase` is set to `False`\n\n#### Case 2 (auto_camelcase=True)\n```python\n@key(\"identifier\")\n@key(\"valid_email\")\nclass User(ObjectType):\n    identifier = ID()\n    email = String(name=\"valid_email\")\n\nclass Query(ObjectType):\n    user = Field(User)\n\nschema = build_schema(query=Query, federation_version=LATEST_VERSION) # auto_camelcase Enabled\n```\n\nThis will raise an error `@key, field \"validEmail\" does not exist on type \"User\"`. \nBecause The decorator auto camel-cased the `field` value of key, as schema has `auto_camelcase=True` (default)\n\nTo fix this, pass `auto_case=False` in the `@key`, `@requires` or `@provides` argument\n\n```python\n@key(\"identifier\")\n@key(\"valid_email\", auto_case=False)\nclass User(ObjectType):\n    identifier = ID()\n    email = String(name=\"valid_email\")\n\nclass Query(ObjectType):\n    user = Field(User)\n\nschema = build_schema(query=Query, federation_version=LATEST_VERSION) # auto_camelcase=True\n```\n\n------------------------\n\n## Known Issues\n\n- Using `@composeDirective` with `@link` in Federation `v2.6` shows error in rover, rover cli only supports upto `v2.5` as of 16/01/2024\n\n## Contributing\n\n* You can run the unit tests by doing: `make tests`.\n* You can run the integration tests by doing `make integration-build \u0026\u0026 make integration-test`.\n* You can get a development environment (on a Docker container) with `make dev-setup`.\n* You should use `black` to format your code.\n\nThe tests are automatically run on Travis CI on push to GitHub.\n\n---------------------------\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgraphql-python%2Fgraphene-federation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgraphql-python%2Fgraphene-federation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgraphql-python%2Fgraphene-federation/lists"}