async-graphql/docs/en/src/apollo_federation.md

4.3 KiB

Apollo Federation

Apollo Federation is a GraphQL API gateway which can combine multiple GraphQL services, allowing each service to implement the subset of the API it is responsible for. You can read more in the official documentation.

Async-graphql supports all the functionality of Apollo Federation, but some modifications to your Schema are required.

  • You can use the extends property declaration on async_graphql::Object and async_graphql::Interface to extend a type offered by another implementing service.

  • The external property declares that a field comes from another service。

  • The provides directive is used to annotate the expected returned fieldset from a field on a base type that is guaranteed to be selectable by the gateway.

  • The requires directive is used to annotate the required input fieldset from a base type for a resolver. It is used to develop a query plan where the required fields may not be needed by the client, but the service may need additional information from other services.

  • The shareable directive is used to indicate that an object type's field is allowed to be resolved by multiple subgraphs (by default, each field can be resolved by only one subgraph).

  • The inaccessible directive is used to indicate that a location in the schema cannot be queried at the supergraph level, but can still be queried at the subgraph level.

  • The tag directive is used to provide a mechanism for applying arbitrary string metadata to the fields and types of a schema. Tags will be propagated up into composed supergraphs.

  • The override directive is used to indicate that a field is now to be resolved by the current subgraph instead of the named subgraph.

  • The link directive is needed in order to indicate that the subgraph is Federation v2 compatible, enabling the shareable, inaccessable, and override directives.

async-graphql provides a configuration function enable_apollo_fed2_link on the schema builder to have it print out an extend schema element with an appropriately configured link directive whenever the federation schema is requested.

Schema::build(Query, EmptyMutation, EmptySubscription)
    .enable_apollo_fed2_link()
    .finish()

and the following (or similar) will be attached to the schema:

extend schema @link(
    url: "https://specs.apollo.dev/federation/v2.0",
    import: ["@key", "@tag", "@shareable", "@inaccessible", "@override", "@external", "@provides", "@requires"]
)

Entity lookup function

# extern crate async_graphql;
# use async_graphql::*;
# #[derive(SimpleObject)]
# struct User { id: ID }
struct Query;

#[Object]
impl Query {
    #[graphql(entity)]
    async fn find_user_by_id(&self, id: ID) -> User {
        User { id }
    }

    #[graphql(entity)]
    async fn find_user_by_id_with_username(&self, #[graphql(key)] id: ID, username: String) -> User {
        User { id }
    }

    #[graphql(entity)]
    async fn find_user_by_id_and_username(&self, id: ID, username: String) -> User {
        User { id }
    }
}

Notice the difference between these three lookup functions, which are all looking for the User object.

  • find_user_by_id

    Use id to find an User object, the key for User is id.

  • find_user_by_id_with_username

    Use id to find an User object, the key for User is id, and the username field value of the User object is requested.

  • find_user_by_id_and_username

    Use id and username to find an User object, the keys for User are id and username.

For a complete example, refer to: https://github.com/async-graphql/examples/tree/master/federation.

Defining a compound primary key

A single primary key can consist of multiple fields, and even nested fields, you can use InputObject to implements a nested primary key.

In the following example, the primary key of the User object is key { a b }.

# extern crate async_graphql;
# use async_graphql::*;
# #[derive(SimpleObject)]
# struct User { id: i32 }
#[derive(InputObject)]
struct NestedKey {
  a: i32,
  b: i32,
}

struct Query;

#[Object]
impl Query {
  #[graphql(entity)]
  async fn find_user_by_key(&self, key: NestedKey) -> User {
    User { id: key.a }
  }
}