Skip to content

Commit

Permalink
Support Apollo Federation v2, except for federated tracing
Browse files Browse the repository at this point in the history
  • Loading branch information
cappuc authored Nov 27, 2023
1 parent 889c7bc commit cd01eef
Show file tree
Hide file tree
Showing 21 changed files with 654 additions and 37 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ You can find and compare releases at the [GitHub release page](https://github.co

## Unreleased

## v6.23.0

### Added

- Support Apollo Federation v2, except for federated tracing https://github.com/nuwave/lighthouse/pull/2460

## v6.22.0

### Added
Expand Down
66 changes: 65 additions & 1 deletion docs/6/federation/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Federation enables you to combine GraphQL services into a single unified data graph.
Read more about the core concepts and motivation in the [Apollo Federation docs](https://www.apollographql.com/docs/federation).

Lighthouse can act as a federation capable service as described in the [Apollo Federation specification](https://www.apollographql.com/docs/federation/federation-spec).
Lighthouse can act as a federation capable service as described in the [Apollo Federation specification](https://www.apollographql.com/docs/federation/federation-spec) v2.
It can not serve as a [federation gateway](https://www.apollographql.com/docs/federation/gateway).

## Setup
Expand All @@ -25,3 +25,67 @@ In order to generate a `.graphql` schema file suitable for publishing, use the `
```sh
php artisan lighthouse:print-schema --federation
```

## Apollo Federation v2

Support for Apollo Federation v2 is `opt-in` and can be enabled by adding the following to your schema.
See [the Apollo documentation on federated directives](https://www.apollographql.com/docs/federation/federated-types/federated-directives) for the latest spec.

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

### Unsupported features

Some features of the Apollo Federation specification **are not supported** by Lighthouse:

#### Renaming directives

Renaming imported directives is not supported.
You can only use the default names.

```graphql
extend schema
@link(
url: "https://specs.apollo.dev/federation/v2.3",
import: [
{ name: "@key", as: "@uniqueKey" },
"@shareable"
]
)
```

#### Namespaced directives

Using directives from a namespace without an import is not supported.
You should import the directive and use the default name.

```graphql
extend schema @link(
url: "https://specs.apollo.dev/federation/v2.3",
import: ["@key"]
)

type Book @federation__shareable {
title: String!
}
```

#### Federated tracing

[Federated tracing](https://www.apollographql.com/docs/federation/metrics) is not supported.
66 changes: 65 additions & 1 deletion docs/master/federation/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Federation enables you to combine GraphQL services into a single unified data graph.
Read more about the core concepts and motivation in the [Apollo Federation docs](https://www.apollographql.com/docs/federation).

Lighthouse can act as a federation capable service as described in the [Apollo Federation specification](https://www.apollographql.com/docs/federation/federation-spec).
Lighthouse can act as a federation capable service as described in the [Apollo Federation specification](https://www.apollographql.com/docs/federation/federation-spec) v2.
It can not serve as a [federation gateway](https://www.apollographql.com/docs/federation/gateway).

## Setup
Expand All @@ -25,3 +25,67 @@ In order to generate a `.graphql` schema file suitable for publishing, use the `
```sh
php artisan lighthouse:print-schema --federation
```

## Apollo Federation v2

Support for Apollo Federation v2 is `opt-in` and can be enabled by adding the following to your schema.
See [the Apollo documentation on federated directives](https://www.apollographql.com/docs/federation/federated-types/federated-directives) for the latest spec.

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

### Unsupported features

Some features of the Apollo Federation specification **are not supported** by Lighthouse:

#### Renaming directives

Renaming imported directives is not supported.
You can only use the default names.

```graphql
extend schema
@link(
url: "https://specs.apollo.dev/federation/v2.3",
import: [
{ name: "@key", as: "@uniqueKey" },
"@shareable"
]
)
```

#### Namespaced directives

Using directives from a namespace without an import is not supported.
You should import the directive and use the default name.

```graphql
extend schema @link(
url: "https://specs.apollo.dev/federation/v2.3",
import: ["@key"]
)

type Book @federation__shareable {
title: String!
}
```

#### Federated tracing

[Federated tracing](https://www.apollographql.com/docs/federation/metrics) is not supported.
4 changes: 3 additions & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ parameters:
- path: tests/database/factories/*
message: '#Variable \$factory might not be defined#'


# Mixins are magical
- path: src/Testing/TestResponseMixin.php
message: '#Method Nuwave\\Lighthouse\\Testing\\TestResponseMixin::assertGraphQLErrorMessage\(\) invoked with 1 parameter, 0 required\.#'
Expand Down Expand Up @@ -60,3 +59,6 @@ parameters:

# Seems like a bug in PHPStan
- '#Parameter \#2 \$type of method Nuwave\\Lighthouse\\Schema\\TypeRegistry::.+Lazy\(\) expects callable\(\): GraphQL\\Type\\Definition\\Type&GraphQL\\Type\\Definition\\NamedType, Closure\(\): GraphQL\\Type\\Definition\\ObjectType given\.#'

# Older versions of bensampo/laravel-enum are not generic yet
- '#contains generic type BenSampo\\Enum\\Enum<.+> but class BenSampo\\Enum\\Enum is not generic\.#'
30 changes: 30 additions & 0 deletions src/Federation/Directives/ComposeDirectiveDirective.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php declare(strict_types=1);

namespace Nuwave\Lighthouse\Federation\Directives;

use Nuwave\Lighthouse\Schema\Directives\BaseDirective;

class ComposeDirectiveDirective extends BaseDirective
{
public const NAME = 'composeDirective';

public static function definition(): string
{
return /* @lang GraphQL */ <<<'GRAPHQL'
"""
Indicates to composition that all uses of a particular custom type system directive
in the subgraph schema should be preserved in the supergraph schema
(by default, composition omits most directives from the supergraph schema).
```graphql
extend schema
@link(url: "https://myspecs.dev/myDirective/v1.0", import: ["@myDirective"])
@composeDirective(name: "@myDirective")
```
https://www.apollographql.com/docs/federation/federated-types/federated-directives#composedirective
"""
directive @composeDirective(name: String!) repeatable on SCHEMA
GRAPHQL;
}
}
40 changes: 40 additions & 0 deletions src/Federation/Directives/InaccessibleDirective.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php declare(strict_types=1);

namespace Nuwave\Lighthouse\Federation\Directives;

use Nuwave\Lighthouse\Schema\Directives\BaseDirective;

class InaccessibleDirective extends BaseDirective
{
public const NAME = 'inaccessible';

public static function definition(): string
{
return /* @lang GraphQL */ <<<'GRAPHQL'
"""
Indicates that a definition in the subgraph schema should be omitted from the router's API schema,
even if that definition is also present in other subgraphs.
This means that the field is not exposed to clients at all.
```graphql
type Position @shareable {
x: Int!
y: Int!
z: Int! @inaccessible
}
```
An @inaccessible field or type is not omitted from the supergraph schema, so the router
still knows it exists (but clients can't include it in operations).
This is what enables the router to use an @inaccessible field as part of an entity's @key
when combining entity fields from multiple subgraphs.
If a type is marked @inaccessible, all fields that return that type must also be marked @inaccessible.
Otherwise, a composition error occurs.
https://www.apollographql.com/docs/federation/federated-types/federated-directives#inaccessible
"""
directive @inaccessible on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
GRAPHQL;
}
}
26 changes: 26 additions & 0 deletions src/Federation/Directives/InterfaceObjectDirective.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php declare(strict_types=1);

namespace Nuwave\Lighthouse\Federation\Directives;

use Nuwave\Lighthouse\Schema\Directives\BaseDirective;

class InterfaceObjectDirective extends BaseDirective
{
public const NAME = 'interfaceObject';

public static function definition(): string
{
return /* @lang GraphQL */ <<<'GRAPHQL'
"""
Indicates that an object definition serves as an abstraction of another subgraph's entity interface.
This abstraction enables a subgraph to automatically contribute fields to all entities that implement a particular entity interface.
During composition, the fields of every @interfaceObject are added both to their corresponding interface definition
and to all entity types that implement that interface.
https://www.apollographql.com/docs/federation/federated-types/federated-directives#interfaceobject
"""
directive @interfaceObject on OBJECT
GRAPHQL;
}
}
27 changes: 27 additions & 0 deletions src/Federation/Directives/LinkDirective.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php declare(strict_types=1);

namespace Nuwave\Lighthouse\Federation\Directives;

use Nuwave\Lighthouse\Schema\Directives\BaseDirective;

class LinkDirective extends BaseDirective
{
public const NAME = 'link';

public static function definition(): string
{
return /* @lang GraphQL */ <<<'GRAPHQL'
"""
This directive links definitions from an external specification to this schema.
Every Federation 2 subgraph uses the @link directive to import the other federation-specific directives.
```graphql
extend schema @link(url: "https://specs.apollo.dev/federation/v2.3")
```
https://www.apollographql.com/docs/federation/federated-types/federated-directives#the-link-directive
"""
directive @link(url: String!, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
GRAPHQL;
}
}
38 changes: 38 additions & 0 deletions src/Federation/Directives/OverrideDirective.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php declare(strict_types=1);

namespace Nuwave\Lighthouse\Federation\Directives;

use Nuwave\Lighthouse\Schema\Directives\BaseDirective;

class OverrideDirective extends BaseDirective
{
public const NAME = 'override';

public static function definition(): string
{
return /* @lang GraphQL */ <<<'GRAPHQL'
"""
Indicates that an object field is now resolved by this subgraph instead of another subgraph
where it's also defined. This enables you to migrate a field from one subgraph to another.
You can apply @override to entity fields and fields of the root operation types (such as Query and Mutation).
```graphql
type Product @key(fields: "id") {
id: ID!
inStock: Boolean! @override(from: "Products")
}
```
You can apply @override to a @shareable field. If you do, only the subgraph you provide
in the from argument no longer resolves that field. Other subgraphs can still resolve the field.
Only one subgraph can @override any given field.
If multiple subgraphs attempt to @override the same field, a composition error occurs.
https://www.apollographql.com/docs/federation/federated-types/federated-directives#override
"""
directive @override(from: String!) on FIELD_DEFINITION
GRAPHQL;
}
}
42 changes: 42 additions & 0 deletions src/Federation/Directives/ShareableDirective.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php declare(strict_types=1);

namespace Nuwave\Lighthouse\Federation\Directives;

use Nuwave\Lighthouse\Schema\Directives\BaseDirective;

class ShareableDirective extends BaseDirective
{
public const NAME = 'shareable';

public static function definition(): string
{
return /* @lang GraphQL */ <<<'GRAPHQL'
"""
Indicates that an object type's field is allowed to be resolved by multiple subgraphs
(by default in Federation 2, object fields can be resolved by only one subgraph).
If applied to an object type definition, all of that type's fields are considered @shareable.
```graphql
type Position {
x: Int! @shareable
y: Int! @shareable
}
type Position @shareable {
x: Int!
y: Int!
}
```
If a field is marked @shareable in any subgraph, it must be marked as either
@shareable or @external in every Federation 2 subgraph that defines it.
If a field is included in an entity's @key directive, that field is automatically
considered @shareable and the directive is not required in the corresponding subgraph(s).
https://www.apollographql.com/docs/federation/federated-types/federated-directives#shareable
"""
directive @shareable on FIELD_DEFINITION | OBJECT
GRAPHQL;
}
}
Loading

0 comments on commit cd01eef

Please sign in to comment.