Skip to content

Commit

Permalink
docs: update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
mak626 committed Jan 16, 2024
1 parent 18ae1d3 commit 0f491aa
Showing 1 changed file with 217 additions and 12 deletions.
229 changes: 217 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,90 @@ Federation support for ![Graphene Logo](http://graphene-python.org/favicon.png)

This 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).


WARNING: This version is not compatible with `graphene` version below v3.
If you need to use a version compatible with `graphene` v2 I recommend using the version 1.0.0 of `graphene_federation`.

------------------------

## Supported Features

At the moment it supports:

* `sdl` (`_service` on field): enable to add schema in federation (as is)
* `@key` decorator (entity support): enable to perform queries across service boundaries (you can have more than one key per type)
* `@extends`: extend remote types
* `external()`: mark a field as external
* `requires()`: mark that field resolver requires other fields to be pre-fetched
* `provides()`/`@provides`: annotate the expected returned fieldset from a field on a base type that is guaranteed to be selectable by the gateway.

## Apollo Spec Supported

- [x] v1.0
- [x] v2.0
- [x] v2.1
- [x] v2.2
- [x] v2.3
- [x] v2.4
- [x] v2.5
- [x] v2.6

All directives could be easily integrated with the help of [graphene-directives](https://github.com/strollby/graphene-directives).
Now every directive's values are validated at run time itself by [graphene-directives](https://github.com/strollby/graphene-directives).

### Directives (v2.6)

```graphql
directive @composeDirective(name: String!) repeatable on SCHEMA
directive @extends on OBJECT | INTERFACE
directive @external on OBJECT | FIELD_DEFINITION
directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE
directive @inaccessible on
| FIELD_DEFINITION
| OBJECT
| INTERFACE
| UNION
| ENUM
| ENUM_VALUE
| SCALAR
| INPUT_OBJECT
| INPUT_FIELD_DEFINITION
| ARGUMENT_DEFINITION
directive @interfaceObject on OBJECT
directive @override(from: String!) on FIELD_DEFINITION
directive @provides(fields: FieldSet!) on FIELD_DEFINITION
directive @requires(fields: FieldSet!) on FIELD_DEFINITION
directive @shareable repeatable on FIELD_DEFINITION | OBJECT
directive @tag(name: String!) repeatable on
| FIELD_DEFINITION
| INTERFACE
| OBJECT
| UNION
| ARGUMENT_DEFINITION
| SCALAR
| ENUM
| ENUM_VALUE
| INPUT_OBJECT
| INPUT_FIELD_DEFINITION
directive @authenticated on
FIELD_DEFINITION
| OBJECT
| INTERFACE
| SCALAR
| ENUM
directive @requiresScopes(scopes: [[Scope!]!]!) on
FIELD_DEFINITION
| OBJECT
| INTERFACE
| SCALAR
| ENUM
directive @policy(policies: [[federation__Policy!]!]!) on
| FIELD_DEFINITION
| OBJECT
| INTERFACE
| SCALAR
| ENUM
scalar federation__Policy
scalar Scope
scalar FieldSet

```

Read about directives in [official documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives)


Each type which is decorated with `@key` or `@extends` is added to the `_Entity` union.
The [`__resolve_reference` method](https://www.apollographql.com/docs/federation/api/apollo-federation/#__resolvereference) can be defined for each type that is an entity.
Expand All @@ -58,8 +127,10 @@ First add an account service that expose a `User` type that can then be referenc

```python
from graphene import Field, Int, ObjectType, String

from graphene_federation import build_schema, key


@key("id")
class User(ObjectType):
id = Int(required=True)
Expand All @@ -71,19 +142,23 @@ class User(ObjectType):
"""
return User(id=self.id, email=f"user_{self.id}@mail.com")


class Query(ObjectType):
me = Field(User)

schema = build_schema(query=Query)

schema = build_schema(query=Query, enable_federation_2=True)
```

### Product
The product service exposes a `Product` type that can be used by other services via the `upc` field:

```python
from graphene import Argument, Int, List, ObjectType, String

from graphene_federation import build_schema, key


@key("upc")
class Product(ObjectType):
upc = String(required=True)
Expand All @@ -96,10 +171,12 @@ class Product(ObjectType):
"""
return Product(upc=self.upc, name=f"product {self.upc}")


class Query(ObjectType):
topProducts = List(Product, first=Argument(Int, default_value=5))

schema = build_schema(query=Query)

schema = build_schema(query=Query, enable_federation_2=True)
```

### Reviews
Expand All @@ -110,7 +187,7 @@ On top of that it adds to the `User`/`Product` types (that are both defined in o
```python
from graphene import Field, Int, List, ObjectType, String

from graphene_federation import build_schema, extends, external, key, provides
from graphene_federation import build_schema, external, key, provides


@key("id")
Expand Down Expand Up @@ -141,7 +218,7 @@ class Query(ObjectType):
review = Field(Review)


schema = build_schema(query=Query)
schema = build_schema(query=Query, enable_federation_2=True)
```

### Federation
Expand Down Expand Up @@ -171,8 +248,136 @@ You can find more examples in the unit / integration tests and [examples folder]
There is also a cool [example](https://github.com/preply/graphene-federation/issues/1) of integration with Mongoengine.

------------------------
## Other Notes

### build_schema new arguments

- `schema_directives` (`Collection[SchemaDirective]`): Directives that can be defined at `DIRECTIVE_LOCATION.SCHEMA`
with their argument values.
- `include_graphql_spec_directives` (`bool`): Includes directives defined by GraphQL spec (`@include`, `@skip`, `@deprecated`, `@specifiedBy`)
- `enable_federation_2` (`bool`): Whether to enable federation 2 directives (default False)
- `federation_version` (`FederationVersion`): Specify the version explicit (default LATEST_VERSION)

In case both enable_federation_2 and federation_version are specified, federation_version is given higher priority

### Directives Additional arguments

- `federation_version`: (`FederationVersion` = `LATEST_VERSION`) : You can use this to take a directive from a particular federation version

Note: The `federation_version` in `build_schema` is given higher priority. If the directive you have chosen is not compatible, it will raise an error

### Custom Directives

You can define custom directives as follows

```python
from graphene import Field, ObjectType, String
from graphql import GraphQLArgument, GraphQLInt, GraphQLNonNull

from graphene_federation import DirectiveLocation, FederationDirective
from graphene_federation import build_schema

CacheDirective = FederationDirective(
name="cache",
locations=[DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.OBJECT],
args={
"maxAge": GraphQLArgument(
GraphQLNonNull(GraphQLInt), description="Specifies the maximum age for cache in seconds."
),
},
description="Caching directive to control cache behavior.",
spec_url="https://specs.example.dev/directives/v1.0",
)

cache = CacheDirective.decorator()


@cache(max_age=20)
class Review(ObjectType):
body = cache(field=String(),max_age=100)


class Query(ObjectType):
review = Field(Review)


schema = build_schema(
query=Query,
directives=(CacheDirective,),
enable_federation_2=True,
)
```

This will automatically add @link and @composeDirective to schema


```graphql
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@composeDirective"])
@link(url: "https://specs.example.dev/directives/v1.0", import: ["@cache"])
@composeDirective(name: "@cache")

"""Caching directive to control cache behavior."""
directive @cache(
"""Specifies the maximum age for cache in seconds."""
maxAge: Int!
) on FIELD_DEFINITION | OBJECT

type Query {
review: Review
_service: _Service!
}

type Review @cache(maxAge: 20) {
body: String @cache(maxAge: 100)
}
```

If you wish to add the schema_directives `@link` `@composeDirective` manually.
You can pass the `add_to_schema_directives` as `False`

```python
from graphene import Field, ObjectType, String
from graphql import GraphQLArgument, GraphQLInt, GraphQLNonNull

from graphene_federation import DirectiveLocation, FederationDirective, build_schema, compose_directive, link_directive

CacheDirective = FederationDirective(
name="cache",
locations=[DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.OBJECT],
args={
"maxAge": GraphQLArgument(
GraphQLNonNull(GraphQLInt), description="Specifies the maximum age for cache in seconds."
),
},
description="Caching directive to control cache behavior.",
add_to_schema_directives=False
)

cache = CacheDirective.decorator()


@cache(max_age=20)
class Review(ObjectType):
body = cache(field=String(), max_age=100)


class Query(ObjectType):
review = Field(Review)


schema = build_schema(
query=Query,
directives=(CacheDirective,),
schema_directives=(
link_directive(url="https://specs.example.dev/directives/v1.0", import_=['@cache']),
compose_directive(name='@cache'),
),
enable_federation_2=True,
)
```

## Custom field name
### Custom field name

When using decorator on a field with custom name

Expand Down

0 comments on commit 0f491aa

Please sign in to comment.