Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs (GraphQL): 21.03 feature documentation for federation #104

Merged
merged 6 commits into from
Mar 18, 2021
Merged
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 163 additions & 0 deletions content/graphql/federation/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
+++
title = "Apollo Federation"
description = "Dgraph now supports Apollo federation so that you can create a gateway GraphQL service that includes the Dgraph GraphQL API and other GraphQL services."
weight = 14
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a "description" under "title" to give us a description for SEO. Suggestion:

description = "Dgraph now supports Apollo federation so that you can create a gateway GraphQL service that includes the Dgraph GraphQL API and other GraphQL services."

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding this!

[menu.main]
name = "Apollo Federation"
identifier = "federation"
parent = "graphql"
+++

Dgraph supports Apollo federation starting in release version 21.03. This lets you create a gateway GraphQL service that includes the Dgraph GraphQL API and other GraphQL services.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Dgraph supports Apollo federation starting in release version 21.03. This lets you create a gateway GraphQL service that includes the Dgraph GraphQL API and other GraphQL services.
Dgraph supports [Apollo federation](https://www.apollographql.com/docs/federation/) starting in release version 21.03. This lets you create a gateway GraphQL service that includes the Dgraph GraphQL API and other GraphQL services.


## Support for Apollo federation directives

The current implementation supports the following five directives: `@key`, `@extends`, `@external`, `@provides`, and `@requires`.

### `@key` directive
This directive takes one field argument inside it: the `@key` field. There are few limitations on how to use `@key` directives:

- Users can define the @key directive only once for a type
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Users can define the @key directive only once for a type
- Users can define the `@key` directive only once for a type

- Support for multiple key types is not currently available.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Support for multiple key types is not currently available.
- Support for multiple key fields is not currently available.

- Since the @key field acts as a foreign key to resolve entities from the service where it is extended, the field provided as an argument inside the @key directive should be of ID type or have the @id directive on it.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Since the @key field acts as a foreign key to resolve entities from the service where it is extended, the field provided as an argument inside the @key directive should be of ID type or have the @id directive on it.
- Since the `@key` field acts as a foreign key to resolve entities from the service where it is extended, the field provided as an argument inside the `@key` directive should be of `ID` type or have the `@id` directive on it.


For example -

```graphql
type User @key(fields: "id") {
id: ID!
name: String
}
```

### `@extends` directive
This directive is provided to give support for extended definitions. Suppose the above defined `User` type is defined in some service. Users can extend it to our GraphQL service by using this directive.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This directive is provided to give support for extended definitions. Suppose the above defined `User` type is defined in some service. Users can extend it to our GraphQL service by using this directive.
This directive is provided to give support for extended definitions. Suppose the above defined `User` type is defined in some other service. Users can extend it in Dgraph's GraphQL service by using this directive.

We can also add that the same is also achievable with the extend keyword, i.e., user has the choice to choose between extend type User ... or type User @extends ...

```graphql
type User @key(fields: "id") @extends{
id: ID! @external
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
id: ID! @external
id: String! @id @external

Let's use this, so that we have both the syntax for @key visible to users in docs.

products: [Product]
}
```

### `@external` directive
This directive is used when the given field is not stored in this service. It can only be used on extended type definitions. As it is used above on the `id` field of `User` type.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This directive is used when the given field is not stored in this service. It can only be used on extended type definitions. As it is used above on the `id` field of `User` type.
You use this directive when the given field is not stored in this service. It can only be used on extended type definitions. For example, it is used in the example shown above on the `id` field of the `User` type.


### `@provides` directive
This directive is used on a field that tells the gateway to return a specific fieldSet from the base type while fetching the field.
Copy link
Contributor

@aaroncarey aaroncarey Mar 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This directive is used on a field that tells the gateway to return a specific fieldSet from the base type while fetching the field.
You use this directive on a field that tells the gateway to return a specific fieldset from the base type while fetching the field.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per https://www.apollographql.com/docs/federation/federation-spec/#scalar-_fieldset, we should either write "fieldset" or _FieldSet. The first one makes more sense to me in this context, but I could be wrong.


For example -

```graphql
type Review @key(fields: "id") {
product: Product @provides(fields: "name price")
}

extend type Product @key(fields: "upc") {
upc: String @external
name: String @external
price: Int @external
}
```

While fetching `Review.product` from the `review` service, and if the `name` or `price` is also queried, the gateway will fetch these from the `review` service itself, meaning that it also resolves these fields, even though both fields are `@external`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
While fetching `Review.product` from the `review` service, and if the `name` or `price` is also queried, the gateway will fetch these from the `review` service itself, meaning that it also resolves these fields, even though both fields are `@external`.
While fetching `Review.product` from the `review` service, and if the `name` or `price` is also queried, the gateway will fetch these from the `review` service itself. So, the `review` service also resolves these fields, even though both fields are `@external`.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Breaking this into two sentences makes it easier to parse (and machine-translate)


### `@requires` directive
This directive is used on a field to annotate the fieldSet of the base type. 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This directive is used on a field to annotate the fieldSet of the base type. 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.
You use this directive on a field to annotate the fieldset of the base type. You can use it 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.


For example -

```graphql
extend type User @key(fields: "id") {
id: ID! @external
email: String @external
reviews: [Review] @requires(fields: "email")
}
```

When the gateway fetches `user.reviews` from the `review` service, the gateway will get `user.email` from the `User` service and provide it as an argument to the `_entities` query.

Copy link
Contributor

@abhimanyusinghgaur abhimanyusinghgaur Mar 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About @requires, we also need to add that:

If you feel the need to use `@requires`, you probably also want to add custom logic on such fields. You can do that using `@lambda`/`@custom(http: {...})`

See, this PR for example usage of @requires with @lambda and accordingly add an example here with @lambda: dgraph-io/dgraph#7558

## Generated queries and mutations

In this section, you will see what all queries and mutations will be available to individual service and to the Apollo gateway.

Let's take the below schema as an example -

```graphql
type Mission @key(fields: "id") {
id: ID!
crew: [Astronaut]
designation: String!
startDate: String
endDate: String
}

type Astronaut @key(fields: "id") @extends {
id: ID! @external
missions: [Mission]
}
```

The queries and mutations which are exposed to the gateway are -

```graphql
type Query {
getMission(id: ID!): Mission
queryMission(filter: MissionFilter, order: MissionOrder, first: Int, offset: Int): [Mission]
aggregateMission(filter: MissionFilter): MissionAggregateResult
}

type Mutation {
addMission(input: [AddMissionInput!]!): AddMissionPayload
updateMission(input: UpdateMissionInput!): UpdateMissionPayload
deleteMission(filter: MissionFilter!): DeleteMissionPayload
addAstronaut(input: [AddAstronautInput!]!): AddAstronautPayload
updateAstronaut(input: UpdateAstronautInput!): UpdateAstronautPayload
deleteAstronaut(filter: AstronautFilter!): DeleteAstronautPayload
}
```

The queries for `Astronaut` are not exposed to the gateway since it will be resolved through the `_entities` resolver. Although these queries will be available on the Dgraph GraphQL API endpoint.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The queries for `Astronaut` are not exposed to the gateway since it will be resolved through the `_entities` resolver. Although these queries will be available on the Dgraph GraphQL API endpoint.
The queries for `Astronaut` are not exposed to the gateway because they are resolved through the `_entities` resolver. However, these queries are available on the Dgraph GraphQL API endpoint.


## Mutation for `extended` types
If you want to add an object of `Astronaut` type which is extended in this service.
The mutation `addAstronaut` takes `AddAstronautInput` which is generated as -
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The mutation `addAstronaut` takes `AddAstronautInput` which is generated as -
The mutation `addAstronaut` takes `AddAstronautInput`, which is generated as follows:


```graphql
input AddAstronautInput {
id: ID!
missions: [MissionRef]
}
```

Even though the `id` field is of `ID` type which should be ideally generated internally by Dgraph. In this case, it should be provided as input since currently federated mutations aren't supported. The user should provide the value of `id` same as the value present in the GraphQL service where the type `Astronaut` is defined.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Even though the `id` field is of `ID` type which should be ideally generated internally by Dgraph. In this case, it should be provided as input since currently federated mutations aren't supported. The user should provide the value of `id` same as the value present in the GraphQL service where the type `Astronaut` is defined.
The `id` field is of `ID` type, which is usually generated internally by Dgraph. In this case, it's provided as an input because federated mutations aren't currently supported. The user should provide the same `id` value that is present in the GraphQL service where the type `Astronaut` is defined.


For example, let's take that the type `Astronaut` is defined in some other service `AstronautService` as -
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
For example, let's take that the type `Astronaut` is defined in some other service `AstronautService` as -
For example, let's assume that the type `Astronaut` is defined in some other service, `AstronautService`, as follows:


```graphql
type Astronaut @key(fields: "id") {
id: ID!
name: String!
}
```

When adding an object of type `Astronaut`, first it should be added into `AstronautService` service and then the `addAstronaut` mutation should be called with value of `id` provided as an argument which must be equal to the value in `AstronautService` service.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
When adding an object of type `Astronaut`, first it should be added into `AstronautService` service and then the `addAstronaut` mutation should be called with value of `id` provided as an argument which must be equal to the value in `AstronautService` service.
When adding an object of type `Astronaut`, you should first add it to the `AstronautService` service. Then, you can call the `addAstronaut` mutation with the value of `id` provided as an argument that must be equal to the value in `AstronautService` service.


Use the admin endpoint to query for the generated schema -
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove the getGQLSchema thing from here, it is not relevant.


```graphql
{
getGQLSchema {
generatedSchema
}
}
```

{{% notice "note" %}}
Currently we only support federated queries, not federated mutations. We might support it in the future.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The apollo spec itself doesn't support federated mutations. So, I don't think we should even say that we might support them later.

{{% /notice %}}

## Gateway supported directives
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel this section is also irrelevant to users. They shouldn't be concerned about the bugs in the underlying libs. They only want to use the feature.
So, let's remove this section too.


Due to the bug in the federation library (see [here](https://github.com/apollographql/federation/issues/346)), some directives are removed from the schema `SDL` which is returned to the gateway in response to the `_service` query. Those directives are `@custom`, `@generate`, and `@auth`.

You can still use these directives in your GraphQL schema and they will work as desired but the gateway will unaware of this.