Slide 1

Slide 1 text

Go GraphQL in LINE SPOT Golang Taipei Gathering #40

Slide 2

Slide 2 text

Hello, I'm Denny. I work at LINE .

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

2019 Q3

Slide 6

Slide 6 text

API Gateway with Go & GraphQL for Microservices

Slide 7

Slide 7 text

API Gateway Go GraphQL

Slide 8

Slide 8 text

Client to Monolith

Slide 9

Slide 9 text

Client to Microservices HTTP HTTP HTTP HTTP HTTP HTTP

Slide 10

Slide 10 text

Client to Microservices HTTP HTTP HTTP gRPC Thrift xRPC

Slide 11

Slide 11 text

Client to API Gateway to Microservices backend for frontend

Slide 12

Slide 12 text

Client to API Gateway

Slide 13

Slide 13 text

REST API

Slide 14

Slide 14 text

https://api.github.com/search/repositories?q=language:go&sort=stars&order=desc { "items": [ { "id": 23096959, "node_id": "MDEwOlJlcG9zaXRvcnkyMzA5Njk1OQ==", "name": "go", "full_name": "golang/go", "owner": { "login": "golang" }, "url": "https://api.github.com/repos/golang/go", "description": "The Go programming language", "issues_url": "https://api.github.com/repos/golang/go/issues{/number}", "stargazers_count": 56341 } ] }

Slide 15

Slide 15 text

https://api.github.com/repos/golang/go/issues [ { "url": "https://api.github.com/repos/golang/go/issues/31459", "number": 31459, "title": "cmd/link: Apple's symbols tool unable to read DWARF data from c-archive go.o", "user": { "login": "tmm1", "avatar_url": "https://avatars2.githubusercontent.com/u/2567?v=4", }, "state": "open" }, { "url": "https://api.github.com/repos/golang/go/issues/31458", "number": 31458, "title": "cmd/go: mod meta tag causes infinite loop in GOPROXY", "user": { "login": "marwan-at-work", "avatar_url": "https://avatars0.githubusercontent.com/u/16294261?v=4", }, "state": "open" } ]

Slide 16

Slide 16 text

Client to API Gateway

Slide 17

Slide 17 text

Multiple Client Types to API Gateway

Slide 18

Slide 18 text

Consumer centric API

Slide 19

Slide 19 text

GraphQL

Slide 20

Slide 20 text

Query language

Slide 21

Slide 21 text

Single endpoint

Slide 22

Slide 22 text

Response shape defined by the client

Slide 23

Slide 23 text

Transport agnostic

Slide 24

Slide 24 text

{ repository( owner: "golang" name: "go" ) { name issues( first: 2 orderBy: { field: CREATED_AT direction: DESC } ) { totalCount nodes { number title } } } } { "data": { "repository": { "name": "go", "issues": { "totalCount": 30761, "nodes": [ { "number": 31459, "title": "cmd/link: Apple's symbols tool ..." }, { "number": 31458, "title": "cmd/go: mod meta tag causes ..." } ] } } } }

Slide 25

Slide 25 text

{ repository( owner: "golang" name: "go" ) { name issues( first: 2 orderBy: { field: CREATED_AT direction: DESC } ) { totalCount nodes { number title } } } } { "data": { "repository": { "name": "go", "issues": { "totalCount": 30761, "nodes": [ { "number": 31459, "title": "cmd/link: Apple's symbols tool ..." }, { "number": 31458, "title": "cmd/go: mod meta tag causes ..." } ] } } } }

Slide 26

Slide 26 text

Queries and Mutations

Slide 27

Slide 27 text

GraphQL schema is typed

Slide 28

Slide 28 text

query { repository( owner: String! name: String! ): Repository } type Repository { name: String! issues( orderBy: IssueOrder first: Int ): IssueConnection! } type IssueConnection { totalCount: Int! nodes: [Issue] } type Issue { number: Int! title: String! } { repository( owner: "golang" name: "go" ) { name issues( first: 2 orderBy: { field: CREATED_AT direction: DESC } ) { totalCount nodes { number title } } } } type IssueOrder { field: IssueOrderField! direction: OrderDirection! } enum IssueOrderField { CREATED_AT UPDATED_AT COMMENTS } enum OrderDirection { ASC DESC }

Slide 29

Slide 29 text

mutation { createIssue( input: CreateIssueInput! ): CreateIssuePayload } type CreateIssuePayload { clientMutationId: String issue: Issue } input CreateIssueInput { repositoryId: ID! title: String! body: String assigneeIds: [ID!] milestoneId: ID labelIds: [ID!] projectIds: [ID!] clientMutationId: String } mutation { createIssue( input: { repositoryId: "..." title: "New issue" body: "This is a new issue" } ) { clientMutationId issue { number title } } }

Slide 30

Slide 30 text

graphql.org

Slide 31

Slide 31 text

Tooling

Slide 32

Slide 32 text

GraphiQL

Slide 33

Slide 33 text

GraphQL Playground

Slide 34

Slide 34 text

Create a GraphQL server with Go

Slide 35

Slide 35 text

graphql-go/graphql graph-gophers/graphql-go 99designs/gqlgen

Slide 36

Slide 36 text

gqlgen

Slide 37

Slide 37 text

Schema first

Slide 38

Slide 38 text

Type safe

Slide 39

Slide 39 text

Less boilerplate

Slide 40

Slide 40 text

Create a schema type Query { posts: [Post] post(id: Int!): Post } type Post { id: Int! title: String! body: String! }

Slide 41

Slide 41 text

Initialize with gqlgen ➜ apigateway go run github.com/99designs/gqlgen init Exec "go run ./server/server.go" to start GraphQL server ➜ apigateway tree . ├── generated.go ├── go.mod ├── go.sum ├── gqlgen.yml ├── models_gen.go ├── resolver.go ├── schema.graphql └── server └── server.go

Slide 42

Slide 42 text

Resolver Interface type QueryResolver interface { Posts(ctx context.Context) ([]*Post, error) Post(ctx context.Context, id int) (*Post, error) }

Slide 43

Slide 43 text

Resolvers type Resolver struct{} func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } type queryResolver struct{ *Resolver } func (r *queryResolver) Posts(ctx context.Context) ([]*Post, error) { panic("not implemented") } func (r *queryResolver) Post(ctx context.Context, id int) (*Post, error) { panic("not implemented") }

Slide 44

Slide 44 text

Generated models type Post struct { ID int `json:"id"` Title string `json:"title"` Body string `json:"body"` }

Slide 45

Slide 45 text

Configuration schema: - schema.graphql exec: filename: generated.go model: filename: models_gen.go resolver: filename: resolver.go type: Resolver models: Post: model: apigateway.Post

Slide 46

Slide 46 text

Generated server const defaultPort = "8080" func main() { port := os.Getenv("PORT") if port == "" { port = defaultPort } http.Handle("/", handler.Playground("GraphQL playground", "/query")) http.Handle("/query", handler.GraphQL(apigateway.NewExecutableSchema(...))) log.Printf("connect to http://localhost:%s/ for GraphQL playground", port) log.Fatal(http.ListenAndServe(":"+port, nil)) }

Slide 47

Slide 47 text

Create a GraphQL schema Initialize with gqlgen Implement resolvers Update GraphQL schema Generate new code with gqlgen Implement new resolvers

Slide 48

Slide 48 text

Demo

Slide 49

Slide 49 text

Authentication Quotas/Rate limiting Request logging ...

Slide 50

Slide 50 text

Hello, I'm YourName. I work at LINE .

Slide 51

Slide 51 text

Thanks!