Skip to content

Commit

Permalink
Update README to include examples from Apollo Federation docs (#100)
Browse files Browse the repository at this point in the history
* Update README to include examples from Apollo Federation docs

Co-authored-by: Doruk Gurleyen <dorukgurleyen@gmail.com>

---------

Co-authored-by: Doruk Gurleyen <dorukgurleyen@gmail.com>
  • Loading branch information
benjaminkimball and kzlsakal authored Jul 31, 2024
1 parent b5e3564 commit 2ae5951
Showing 1 changed file with 140 additions and 35 deletions.
175 changes: 140 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# Absinthe.Federation

[![Build Status](https://github.com/DivvyPayHQ/absinthe_federation/workflows/CI/badge.svg)](https://github.com/DivvyPayHQ/absinthe_federation/actions?query=workflow%3ACI)
[![Hex pm](http://img.shields.io/hexpm/v/absinthe_federation.svg)](https://hex.pm/packages/absinthe_federation)
[![Hex pm](https://img.shields.io/hexpm/v/absinthe_federation.svg)](https://hex.pm/packages/absinthe_federation)
[![Hex Docs](https://img.shields.io/badge/hex-docs-blue.svg)](https://hexdocs.pm/absinthe_federation/)
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)

[Apollo Federation](https://www.apollographql.com/docs/federation/federation-spec/) support for [Absinthe](https://github.com/absinthe-graphql/absinthe)
[Apollo Federation](https://www.apollographql.com/docs/federation) support for [Absinthe](https://hexdocs.pm/absinthe/overview.html).

## Installation

Install from [Hex.pm](https://hex.pm/packages/absinthe_federation):
Install from [Hex](https://hex.pm/packages/absinthe_federation):

```elixir
def deps do
Expand All @@ -19,7 +19,7 @@ def deps do
end
```

Install from github:
Install a specific branch from [GitHub](https://github.com/DivvyPayHQ/absinthe_federation):

```elixir
def deps do
Expand All @@ -29,10 +29,10 @@ def deps do
end
```

Add the following line to your absinthe schema
Use `Absinthe.Federation.Schema` module in your root schema:

```elixir
defmodule MyApp.MySchema do
defmodule Example.Schema do
use Absinthe.Schema
+ use Absinthe.Federation.Schema

Expand All @@ -42,56 +42,160 @@ defmodule MyApp.MySchema do
end
```

## Usage
Validate everything is wired up correctly:

### Macro based schemas (recommended)
```bash
mix absinthe.federation.schema.sdl --schema Example.Schema
```

You should see the [Apollo Federation Subgraph Specification](https://www.apollographql.com/docs/federation/subgraph-spec) fields along with any fields you've defined. It can be helpful to add `*.graphql` to your `.gitignore`, at least at your projects root level, while testing your SDL output during development.

## Usage (macro based schemas)

> Note: Implementing the reference resolver with function capture does not work at the moment. Hence, the below example uses an anonymous function.
The following sticks close to the Apollo Federation documentation to better clarify how to achieve the same outcomes with the `Absinthe.Federation` module as you'd get from their JavaScript examples.

### [Defining an entity](https://www.apollographql.com/docs/federation/entities#defining-an-entity)

```elixir
defmodule MyApp.MySchema do
defmodule Products.Schema do
use Absinthe.Schema
+ use Absinthe.Federation.Schema
use Absinthe.Federation.Schema

query do
+ extends()
extend schema do
directive(:link,
url: "https://specs.apollo.dev/federation/v2.3",
import: ["@key", ...]
)
end

field :review, :review do
arg(:id, non_null(:id))
resolve(&ReviewResolver.get_review_by_id/3)
object :product do
directive :key, fields: "id"

# Any subgraph contributing fields MUST define a _resolve_reference field.
# Note that implementing the reference resolver with function capture does not work at the moment. Hence, the examples below use an anonymous function.
field :_resolve_reference, :product do
resolve(fn %{__typename: "Product", id: id} = entity, _info ->
{:ok, Map.merge(entity, %{name: "ACME Anvil", price: 10000})}
end)
end

field :id, non_null(:id)
field :name, non_null(:string)
field :price, :int
end

query do
...
end
end
```

Your `:_resolve_reference` must return one of the following:

```elixir
{:ok, %Product{id: id, ...}}
```

```elixir
{:ok, %{__typename: "Product", id: id, ...}}
```

```elixir
{:ok, %{"__typename" => "Product", "id" => id, ...}}
```

```elixir
{:ok, nil}
```

It is easier to just merge a subgraph's contributed fields back onto the incoming entity reference than rely on a struct to set the `__typename`.

### [Contributing entity fields](https://www.apollographql.com/docs/federation/entities#contributing-entity-fields)

Each subgraph, by default, must return different fields. See the Apollo documentation should you need to [override this behavior](https://www.apollographql.com/docs/federation/entities/resolve-another-subgraphs-fields).

```elixir
defmodule Inventory.Schema do
use Absinthe.Schema
use Absinthe.Federation.Schema

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

object :product do
+ key_fields("upc")
+ extends()
directive :key, fields: "id"

field :upc, non_null(:string) do
+ external()
# In this case, only the `Inventory.Schema` should resolve the `inStock` field.
field :_resolve_reference, :product do
resolve(fn %{__typename: "Product", id: id} = entity, _info ->
{:ok, Map.merge(entity, %{in_stock: true})}
end)
end

field(:reviews, list_of(:review)) do
resolve(&ReviewResolver.get_reviews_for_product/3)
end
field :id, non_null(:string)
field :in_stock, non_null(:boolean)
end

+ field(:_resolve_reference, :product) do
+ resolve(fn parent, args, context ->
ProductResolver.get_product_by_upc(parent, args, context)
query do
...
end
end
```

### [Referencing an entity without contributing fields](https://www.apollographql.com/docs/federation/entities#referencing-an-entity-without-contributing-fields)

```elixir
defmodule Reviews.Schema do
use Absinthe.Schema
use Absinthe.Federation.Schema

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

# Stubbed entity, marked as unresolvable in this subgraph.
object :product do
directive :key, fields: "id", resolvable: false

field :id, non_null(:string)
end

object :review do
field :id, non_null(:id)
field :score, non_null(:int)
field :description, non_null(:string)

# This subgraph only needs to resolve the key fields used to reference the entity.
field :product, non_null(:product) do
resolve(fn %{product_id: id} = _parent, _args, _info ->
{:ok, %{id: id}}
end)
+ end
end
end

query do
field :latest_reviews, non_null(list(:review)) do
resolve(&ReviewsResolver.find_many/2)
end
end
end
```

### Macro based schema with existing prototype

If you are already using a schema prototype
If you are already using a schema prototype.

```elixir
defmodule MyApp.MySchema do
defmodule Example.Schema do
use Absinthe.Schema
+ use Absinthe.Federation.Schema, prototype_schema: MyApp.MySchemaPrototype
+ use Absinthe.Federation.Schema, prototype_schema: Example.SchemaPrototype

query do
...
Expand All @@ -100,7 +204,7 @@ end
```

```elixir
defmodule MyApp.MySchemaPrototype do
defmodule Example.SchemaPrototype do
use Absinthe.Schema.Prototype
+ use Absinthe.Federation.Schema.Prototype.FederatedDirectives

Expand All @@ -113,7 +217,7 @@ end
### SDL based schemas (experimental)

```elixir
defmodule MyApp.MySchema do
defmodule Example.Schema do
use Absinthe.Schema
+ use Absinthe.Federation.Schema

Expand All @@ -131,6 +235,7 @@ defmodule MyApp.MySchema do
def hydrate(_, _) do
...
end
end
```

### Resolving structs in \_entities queries
Expand All @@ -156,7 +261,7 @@ end
You can import Apollo Federation v2 directives by extending your top-level schema with the `@link` directive.

```elixir
defmodule MyApp.MySchema do
defmodule Example.Schema do
use Absinthe.Schema
use Absinthe.Federation.Schema

Expand Down Expand Up @@ -189,13 +294,13 @@ end
`@link` directive supports namespacing and directive renaming (only on **Absinthe >= 1.7.2**) according to the specs.

```elixir
defmodule MyApp.MySchema do
defmodule Example.Schema do
use Absinthe.Schema
use Absinthe.Federation.Schema

+ extend schema do
+ directive :link,
+ url: "https://specs.apollo.dev/federation/v2.0",
+ url: "https://specs.apollo.dev/federation/v2.3",
+ import: [%{"name" => "@key", "as" => "@primaryKey"}], # directive renaming
+ as: "federation" # namespacing
+ end
Expand Down

0 comments on commit 2ae5951

Please sign in to comment.