GraphQL、gqlgen を使って サーバ を立てる

# 記事を書こうと思ったきっかけ
インターン先で GraphQL が使われているので実際に手元で動かしてみようと思ったのがきっかけです。
本家のチュートリアルや、参考になった記事の内容をもとに構成されています。
この記事は [Go Conference mini 2022 Autumn IN Sendai](https://sendaigo.connpass.com/event/256463/?utm_campaign=event_message_to_selected_participant&utm_source=notifications&utm_medium=email&utm_content=title_link) の発表でも使われています。

# この記事に書かれていること
:::note info
gqlgen を使ったサーバの立て方
:::

:::note alert
Client 側の詳しい説明
:::


# GraphQL とは
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1680061/21e84b28-8f4c-a850-9e9f-f95442a30cf5.png)

- Facebook が作った OSS
- API のために作られたクエリ言語
- 既存のデータに対するクエリを実行するランタイム

# Client 側
↓ のスライドをご覧ください。GraphQL のメリット・デメリットも書いてあります。
<script async class="speakerdeck-embed" data-id="4232a6edc1d54f7d96ef2d534ca6d684" data-ratio="1.77725118483412" src="//speakerdeck.com/assets/embed.js"></script>

# gqlgen とは
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1680061/d6826a01-3b1e-38ca-98a3-a1bc699cdde4.png)

> - gqlgen はGraphQLサーバを簡単に構築するためのGoライブラリです。
> - gqlgen はスキーマファーストのアプローチに基づいています
> - gqlgen は型の安全性を優先します
> - gqlgen はCodegenを可能にします

とりあえず作って試してみよう
# サーバを立てる
[https://github.com/99designs/gqlgen](https://github.com/99designs/gqlgen)

1\. 作業するディレクトリ作成、初期化
```
mkdir gqlgen-server

cd gqlgen-server

go mod init gqlgen-server
```

2\. `github.com/99designs/gqlgen` を `tools.go` に追加
```
printf '// +build tools\npackage tools\nimport (_ "github.com/99designs/gqlgen"\n _ "github.com/99designs/gqlgen/graphql/introspection")' | gofmt > tools.go

go mod tidy
```

3\. gqlgen の設定を初期化し、モデルを生成
```
go run github.com/99designs/gqlgen init
```

4\. gqlgen サーバ起動
```
go run server.go
```

これでサーバを起動しました。
↓ のようなアウトプットになれば成功！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1680061/16c5d9f3-7356-f8cd-4e33-4a4c38afcdaa.png)
http://localhost:8080/ にアクセスすると ↓ のような Client 画面に飛ぶかと思います。
ここでは GraphQL のリクエストである、`query` や `mutation` を書いて、リクエストを投げることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1680061/ddcaab6e-ae1d-3f44-efff-83684184e5d4.png)



## ディレクトリ構成
```
├── go.mod
├── go.sum
├── gqlgen.yml               - gqlgen の設定ファイル。生成されたコードを制御する
├── graph
│   ├── generated            - 生成されたランタイムのみが含まれるパッケージ
│   │   └── generated.go
│   ├── model                - すべての graph model 用のパッケージ
│   │   └── models_gen.go
│   ├── resolver.go          - root graph resolver タイプ。このファイルは再生成されない
│   ├── schema.graphqls      - スキーマが書かれているファイル。好きなようにいじって大丈夫
│   └── schema.resolvers.go  - schema.graphql の resolver 実装。
└── server.go                - エントリーポイント。カスタマイズできる
```

# 実践パート
ファイルを見てみよう

↓ のようなスキーマが生成されているはず。
```schema.graphqls
type Todo {
  id: ID!
  text: String!
  done: Boolean!
  user: User!
}

type User {
  id: ID!
  name: String!
}

type Query {
  todos: [Todo!]!
}

input NewTodo {
  text: String!
  userId: String!
}

type Mutation {
  createTodo(input: NewTodo!): Todo!
}
```

変更するファイル
```graph/resolver.go
type Resolver struct{
    // ↓ の一行を追加 
	todos []*model.Todo
}
```
正常に import できていない packege があるかもしれないので、エラーが出たら確認してみてください。

↓ のように変更する
自動生成されたリゾルバ関数の本体を実装。
CreateTodoについては、math.randパッケージを使用して、ランダムに生成されたIDを持つTodoを返し、それをインメモリのTodoリストに格納。

```graph/schema.resolvers.go
func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
	todo := &model.Todo{
		Text:   input.Text,
		ID:     fmt.Sprintf("T%d", rand.Int()),
		User: &model.User{ID: input.UserID, Name: "user " + input.UserID},
	}
	r.todos = append(r.todos, todo)
	return todo, nil
}

func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) {
	return r.todos, nil
}
```

これでサーバにリクエストを送る準備完了。

再度サーバを起動する
```
go run server.go
```

## todo 作成
http://localhost:8080/ ここで request を書く

request
```
mutation createTodo {
  createTodo(input: { text: "todo", userId: "3" }) {
    user {
      id
    }
    text
    done
  }
}
```

response
```
{
  "data": {
    "createTodo": {
      "user": {
        "id": "3"
      },
      "text": "todo",
      "done": false
    }
  }
}
```

## todo 取得
http://localhost:8080/ ここで request を書く

request

```
query findTodos {
  todos {
    id
    text
    done
    user {
      name
    	id
    }
  }
}
```

response
```
{
  "data": {
    "todos": [
      {
        "id": "T5577006791947779410",
        "text": "todo",
        "done": false,
        "user": {
          "name": "user 3",
          "id": "3"
        }
      }
    ]
  }
}
```

以上！！

# 終わりに
初めて qiita の記事を書いてみました。自分の知識整理のためにもなり勉強になりました！この調子でアウトプットしていけたらいいなと思います。
また、この記事が皆様の役に立つことを祈っています。

博多行きたい。