-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1018 from 99designs/federation-docs
Federation docs and examples
- Loading branch information
Showing
45 changed files
with
10,000 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#!/bin/bash | ||
|
||
set -euo pipefail | ||
|
||
cd example/federation | ||
|
||
./start.sh & | ||
|
||
sleep 10 | ||
|
||
echo "### running jest integration spec" | ||
./node_modules/.bin/jest --color | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
--- | ||
title: 'Using Apollo federation gqlgen' | ||
description: How federate many services into a single graph using Apollo | ||
linkTitle: Apollo Federation | ||
menu: { main: { parent: 'recipes' } } | ||
--- | ||
|
||
In this quick guide we are going to implement the example [Apollo Federation](https://www.apollographql.com/docs/apollo-server/federation/introduction/) | ||
server in gqlgen. You can find the finished result in the [examples directory](/example/federation). | ||
|
||
|
||
## Create the federated servers | ||
|
||
For each server to be federated we will create a new gqlgen project. | ||
|
||
```bash | ||
go run github.com/99designs/gqlgen | ||
``` | ||
|
||
Update the schema to reflect the federated example | ||
```graphql | ||
type Review { | ||
body: String | ||
author: User @provides(fields: "username") | ||
product: Product | ||
} | ||
|
||
type User @extends @key(fields: "id") { | ||
id: ID! @external | ||
reviews: [Review] | ||
} | ||
|
||
type Product @extends @key(fields: "upc") { | ||
upc: String! @external | ||
reviews: [Review] | ||
} | ||
``` | ||
|
||
> Note | ||
> | ||
> gqlgen doesnt currently support `extend type Foo` syntax for apollo federation, we must use | ||
> the `@extends` directive instead. | ||
|
||
|
||
and regenerate | ||
```bash | ||
go run github.com/99designs/gqlgen | ||
``` | ||
|
||
then implement the resolvers | ||
```go | ||
// These two methods are required for gqlgen to resolve the internal id-only wrapper structs. | ||
// This boilerplate might be removed in a future version of gqlgen that can no-op id only nodes. | ||
func (r *entityResolver) FindProductByUpc(ctx context.Context, upc string) (*model.Product, error) { | ||
return &model.Product{ | ||
Upc: upc, | ||
}, nil | ||
} | ||
|
||
func (r *entityResolver) FindUserByID(ctx context.Context, id string) (*model.User, error) { | ||
return &model.User{ | ||
ID: id, | ||
}, nil | ||
} | ||
|
||
// Here we implement the stitched part of this service, returning reviews for a product. Of course normally you would | ||
// go back to the database, but we are just making some data up here. | ||
func (r *productResolver) Reviews(ctx context.Context, obj *model.Product) ([]*model.Review, error) { | ||
switch obj.Upc { | ||
case "top-1": | ||
return []*model.Review{{ | ||
Body: "A highly effective form of birth control.", | ||
}}, nil | ||
|
||
case "top-2": | ||
return []*model.Review{{ | ||
Body: "Fedoras are one of the most fashionable hats around and can look great with a variety of outfits.", | ||
}}, nil | ||
|
||
case "top-3": | ||
return []*model.Review{{ | ||
Body: "This is the last straw. Hat you will wear. 11/10", | ||
}}, nil | ||
|
||
} | ||
return nil, nil | ||
} | ||
|
||
func (r *userResolver) Reviews(ctx context.Context, obj *model.User) ([]*model.Review, error) { | ||
if obj.ID == "1234" { | ||
return []*model.Review{{ | ||
Body: "Has an odd fascination with hats.", | ||
}}, nil | ||
} | ||
return nil, nil | ||
} | ||
``` | ||
|
||
> Note | ||
> | ||
> Repeat this step for each of the services in the apollo doc (accounts, products, reviews) | ||
## Create the federation gateway | ||
|
||
```bash | ||
npm install --save @apollo/gateway apollo-server graphql | ||
``` | ||
|
||
```typescript | ||
const { ApolloServer } = require('apollo-server'); | ||
const { ApolloGateway } = require("@apollo/gateway"); | ||
|
||
const gateway = new ApolloGateway({ | ||
serviceList: [ | ||
{ name: 'accounts', url: 'http://localhost:4001/query' }, | ||
{ name: 'products', url: 'http://localhost:4002/query' }, | ||
{ name: 'reviews', url: 'http://localhost:4003/query' } | ||
], | ||
}); | ||
|
||
const server = new ApolloServer({ | ||
gateway, | ||
|
||
subscriptions: false, | ||
}); | ||
|
||
server.listen().then(({ url }) => { | ||
console.log(`🚀 Server ready at ${url}`); | ||
}); | ||
``` | ||
|
||
## Start all the services | ||
|
||
In separate terminals: | ||
```bash | ||
go run accounts/server.go | ||
go run products/server.go | ||
go run reviews/server.go | ||
node gateway/index.js | ||
``` | ||
|
||
## Query the federated gateway | ||
|
||
The examples from the apollo doc should all work, eg | ||
|
||
```graphql | ||
query { | ||
me { | ||
username | ||
reviews { | ||
body | ||
product { | ||
name | ||
upc | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
should return | ||
|
||
```json | ||
{ | ||
"data": { | ||
"me": { | ||
"username": "Me", | ||
"reviews": [ | ||
{ | ||
"body": "A highly effective form of birth control.", | ||
"product": { | ||
"name": "Trilby", | ||
"upc": "top-1" | ||
} | ||
}, | ||
{ | ||
"body": "Fedoras are one of the most fashionable hats around and can look great with a variety of outfits.", | ||
"product": { | ||
"name": "Trilby", | ||
"upc": "top-1" | ||
} | ||
} | ||
] | ||
} | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
# Where are all the schema files located? globs are supported eg src/**/*.graphqls | ||
schema: | ||
- graph/*.graphqls | ||
|
||
# Where should the generated server code go? | ||
exec: | ||
filename: graph/generated/generated.go | ||
package: generated | ||
|
||
federation: | ||
filename: graph/generated/federation.go | ||
package: generated | ||
|
||
# Where should any generated models go? | ||
model: | ||
filename: graph/model/models_gen.go | ||
package: model | ||
|
||
# Where should the resolver implementations go? | ||
resolver: | ||
layout: follow-schema | ||
dir: graph | ||
package: graph | ||
|
||
# Optional: turn on use `gqlgen:"fieldName"` tags in your models | ||
# struct_tag: json | ||
|
||
# Optional: turn on to use []Thing instead of []*Thing | ||
# omit_slice_element_pointers: false | ||
|
||
# Optional: set to speed up generation time by not performing a final validation pass. | ||
# skip_validation: true | ||
|
||
# gqlgen will search for any type names in the schema in these go packages | ||
# if they match it will use them, otherwise it will generate them. | ||
autobind: | ||
- "github.com/99designs/gqlgen/example/federation/accounts/graph/model" | ||
|
||
# This section declares type mapping between the GraphQL and go type systems | ||
# | ||
# The first line in each type will be used as defaults for resolver arguments and | ||
# modelgen, the others will be allowed when binding to fields. Configure them to | ||
# your liking | ||
models: | ||
ID: | ||
model: | ||
- github.com/99designs/gqlgen/graphql.ID | ||
- github.com/99designs/gqlgen/graphql.Int | ||
- github.com/99designs/gqlgen/graphql.Int64 | ||
- github.com/99designs/gqlgen/graphql.Int32 | ||
Int: | ||
model: | ||
- github.com/99designs/gqlgen/graphql.Int | ||
- github.com/99designs/gqlgen/graphql.Int64 | ||
- github.com/99designs/gqlgen/graphql.Int32 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// This file will be automatically regenerated based on the schema, any resolver implementations | ||
// will be copied through when generating and any unknown code will be moved to the end. | ||
package graph | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/99designs/gqlgen/example/federation/accounts/graph/generated" | ||
"github.com/99designs/gqlgen/example/federation/accounts/graph/model" | ||
) | ||
|
||
func (r *entityResolver) FindUserByID(ctx context.Context, id string) (*model.User, error) { | ||
name := "User " + id | ||
if id == "1234" { | ||
name = "Me" | ||
} | ||
|
||
return &model.User{ | ||
ID: id, | ||
Username: name, | ||
}, nil | ||
} | ||
|
||
func (r *Resolver) Entity() generated.EntityResolver { return &entityResolver{r} } | ||
|
||
type entityResolver struct{ *Resolver } |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.