3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Posted at

前提

  • dockerとdocker-composeインストール済み
  • prisma cli インストール済み

どのような物を作るか?

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

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

まずは、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テーブルが作成されました。

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の中身を確認だけしておきます。

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!
}

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プレイグラウンドが立ち上がっています。

cd gqlkit-server
go run server.go

まとめ

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

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

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

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

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?