Goの30秒で始められるGraphQLサーバー構築

# 前提
- dockerとdocker-composeインストール済み
- prisma cli インストール済み

# どのような物を作るか？
単純に`Hello World!`などのメッセージ一覧を返したり
メッセージを新規作成したり更新、削除したりといった
GraphQLのCRUD APIサーバーを構築します。


#gqlkitをgit cloneし、コンテナを起動する

まずは、[gqlkit](https://github.com/gqlkit-lab/gqlkit)というdockerアプリケーションフレームワークをgit cloneし、コンテナを起動しましょう。
これで、半分は作業が完了しました！

```
git clone git@github.com:gqlkit-lab/gqlkit.git
cd gqlkit
cp .env.example .env
docker-compose up -d
```

# PostgreSQLに、データの雛形をマイグレートする
今回、どのようなデータモデルをマイグレートするのかを確認しておきましょう。
gqlkit-server/prisma/schema.prismaを開くと下記のようになっています。
このファイルを編集して`prisma migrate`してやる事で
データベースにデータモデルを作ることができます。

```gqlkit-server/prisma/schema.prisma
datasource db {
    provider = "postgresql"
    url      = "postgresql://postgres:postgres@localhost:5432/postgres?schema=public"
}

model messages {
    id   String @default(cuid()) @id
    text String
    created_at String
    updated_at String
}
```
今回は、このままの内容をマイグレートするのでファイルの内容は編集せず下記を実行します。
これでPostgreSQLに`messages`テーブルが作成されました。

```shell
prisma migrate save --experimental
prisma migrate up --experimental
```

ちなみに`schema.prisma`に書くモデル名の注意点ですが
gqlkitはgormを採用しているので「messages」のように複数形で「s」が最後に来るように気をつけましょう。
2単語以上の場合はsnake_caseで書いてやります。

# GraphQLサーバーを起動する
次の流れとしては、gqlkit-server/graph/schema.graphqlを編集し
gqlkit-server/graph/schema.resolvers.goにリゾルバを書いていくのですが
gqlkitはデフォルトのサンプルとして
messagesのCRUDサンプルはgqlgenでジェネレート済みとなっています。
なので、ここではさらっと
schema.graphqlの中身とschema.resolvers.goの中身を確認だけしておきます。

```graphql:gqlkit-server/graph/schema.graphql
type Query {
    readMessages: [Message!]!
}

type Mutation {
    createMessage(text: String!): Message!
    updateMessage(id: ID!, text: String!): Message!
    deleteMessage(id: ID!): Message!
}

type Message {
    id: ID!
    text: String!
    created_at: String
    updated_at: String!
}

```

```go:gqlkit-server/graph/schema.resolvers.go
package graph

// 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.

import (
	"context"
	"fmt"
	"gqlkit/env"
	"gqlkit/graph/generated"
	"gqlkit/graph/model"
	"time"

	"github.com/jinzhu/gorm"
	_ "github.com/lib/pq"
	uuid "github.com/satori/go.uuid"
)

func (r *mutationResolver) CreateMessage(ctx context.Context, text string) (*model.Message, error) {
	db, err := gorm.Open("postgres", env.DB_CONNECT)
	defer db.Close()
	if err != nil {
		return nil, fmt.Errorf(err.Error())
	}

	id := uuid.Must(uuid.NewV4(), nil)
	loc, _ := time.LoadLocation("Asia/Tokyo")
	now := time.Now().In(loc).Format("2006-01-02T15:04:05+09:00")

	r.message = &model.Message{
		ID:        id.String(),
		Text:      text,
		CreatedAt: &now,
		UpdatedAt: now,
	}

	db.Create(&r.message)
	return r.message, nil
}

func (r *mutationResolver) UpdateMessage(ctx context.Context, id string, text string) (*model.Message, error) {
	db, err := gorm.Open("postgres", env.DB_CONNECT)
	defer db.Close()
	if err != nil {
		return nil, fmt.Errorf(err.Error())
	}

	loc, _ := time.LoadLocation("Asia/Tokyo")
	now := time.Now().In(loc).Format("2006-01-02T15:04:05+09:00")

	db.Where("id = ?", id).First(&r.messages)
	r.message = r.messages[0]
	db.Model(&r.message).Where("id = ?", id).Update("text", text)
	db.Model(&r.message).Where("id = ?", id).Update("updated_at", now)

	return r.message, nil
}

func (r *mutationResolver) DeleteMessage(ctx context.Context, id string) (*model.Message, error) {
	db, err := gorm.Open("postgres", env.DB_CONNECT)
	defer db.Close()
	if err != nil {
		return nil, fmt.Errorf(err.Error())
	}

	db.Where("id = ?", id).First(&r.messages)
	r.message = r.messages[0]
	db.Where("id = ?", id).Delete(&r.messages)

	return r.message, nil
}

func (r *queryResolver) ReadMessages(ctx context.Context) ([]*model.Message, error) {
	db, err := gorm.Open("postgres", env.DB_CONNECT)
	defer db.Close()
	if err != nil {
		return nil, fmt.Errorf(err.Error())
	}

	db.Order("created_at").Find(&r.messages)

	return r.messages, nil
}

// Mutation returns generated.MutationResolver implementation.
func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} }

// Query returns generated.QueryResolver implementation.
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }

type mutationResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }

```

実際に、任意のアプリを開発する際は、
`schema.graphql`を編集する
↓
`go run github.com/99designs/gqlgen`でリゾルバをジェネレートする
↓
`schema.resolvers.go`を編集する
という感じの流れで開発していきます。

という事で最後に、下記を実行してGraphQLプレイグラウンドを立ち上げましょう！
http://localhost:8080/ でGraphQLプレイグラウンドが立ち上がっています。

```shell
cd gqlkit-server
go run server.go
```



# まとめ
現在、マークアップエンジニアからwebエンジニアへと
ステップアップ転職を目指しているのですが

何を思ったか求人の多いRubyに手を出さず
先にGoに手を出してしまいました。

すると、Goのなんと楽にバックエンド開発が終了してしまう事か
これは、下手をするとfirebase + nuxtでアプリ作ってた時以上に楽です。

Goに触れたのは幸なのか不幸なのか
その後、Railsの学習に手をつけてみようと何度かトライするのですが
Goの圧倒的すぎるシンプルさ、簡単さ、スッキリさ、に引きずられてしまい
どうしても、わざわざ学ぼうという意欲が出ないでいます。
Laravelも同じく...
求人は多いのに...(泣)
Goめ！実に罪深い！(褒め言葉)