Help us understand the problem. What is going on with this article?

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

前提

  • 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め!実に罪深い!(褒め言葉)

Inadati
京都でwebエンジニアやっています。 GraphQLの研究を行っています。 軽いアニオタでミュージシャンでもある 投稿はその時ハマってるアニメの影響を受けます。あしからず! ↓GraphQL研究専用リポジトリ https://github.com/gqlkit-lab ↓サンプルぐちゃぐちゃリポジトリ https://github.com/inadati
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした