<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kenichi Ebinuma</title>
    <description>The latest articles on DEV Community by Kenichi Ebinuma (@ebiken).</description>
    <link>https://dev.to/ebiken</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F102583%2F817c2903-1572-4cac-b629-ecd1271cf5fb.jpeg</url>
      <title>DEV Community: Kenichi Ebinuma</title>
      <link>https://dev.to/ebiken</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ebiken"/>
    <language>en</language>
    <item>
      <title>Create a GraphQL server with Go</title>
      <dc:creator>Kenichi Ebinuma</dc:creator>
      <pubDate>Sat, 16 May 2020 14:02:45 +0000</pubDate>
      <link>https://dev.to/ebiken/create-a-graphql-server-with-go-3mpd</link>
      <guid>https://dev.to/ebiken/create-a-graphql-server-with-go-3mpd</guid>
      <description>&lt;p&gt;Let's create a GraphQL server with Go.&lt;br&gt;
&lt;a href="https://gqlgen.com/"&gt;gqlgen&lt;/a&gt; is a good choice to implement GraphQL server with Go.&lt;/p&gt;

&lt;p&gt;code is here.&lt;br&gt;
&lt;a href="https://github.com/ebkn/graphql-app-advent-calendar-2019"&gt;GitHub&lt;/a&gt;&lt;br&gt;
This is an English version of article posted on &lt;a href="https://qiita.com/ebkn/items/0b30bdbf0dae5df73d2e"&gt;Qiita Advent Calendar 2019&lt;/a&gt;.&lt;/p&gt;


&lt;h1&gt;
  
  
  What is GraphQL?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://graphql.org"&gt;graphql.org&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;GraphQL is a query language and implementation for API. At first it is developed by Facebook, and now promoted by &lt;a href="https://foundation.graphql.org/"&gt;GraphQL Foundation&lt;/a&gt;.&lt;br&gt;
We can take many advantages like type safe request, editor completion, auto-generated documents.&lt;/p&gt;
&lt;h1&gt;
  
  
  What is gqlgen?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://gqlgen.com/"&gt;gqlgen.com&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;gqlgen is a Go library for building GraphQL servers without any fuss.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;gqlgen is GraphQL server library that have following features.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Schema first&lt;/li&gt;
&lt;li&gt;Type safe&lt;/li&gt;
&lt;li&gt;Code generate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Schema first development is a development style. &lt;code&gt;define schema&lt;/code&gt; -&amp;gt; &lt;code&gt;code generate&lt;/code&gt; -&amp;gt; &lt;code&gt;implement specific logic&lt;/code&gt;. Please check [Feature comparison] if you are interested.&lt;/p&gt;


&lt;h1&gt;
  
  
  Features
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;list tasks (with pagination)&lt;/li&gt;
&lt;li&gt;order tasks

&lt;ul&gt;
&lt;li&gt;by &lt;code&gt;created_at&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;by &lt;code&gt;due&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;create task&lt;/li&gt;
&lt;li&gt;update task&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is one example of GraphQL schema for implementing above features.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;query {
  tasks(input: TasksInput!, orderBy: TaskOrderFields!, page: PaginationInput!): TaskConnection!
}

mutation {
  createTask(input: CreateTaskInput!): Task!
  updateTask(input: UpdateTaskInput!): Task!
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type Task implements Node {
  id: ID!
  title: String!
  notes: String!
  completed: Boolean!
  due: Time!
}
type TaskEdge implements Edge {
  cursor: String!
  node: Task!
}
type TaskConnection implements Connection {
  pageInfo: PageInfo!
  edges: [TaskEdge]!
}

input TasksInput {
  completed: Boolean
}

enum TaskOrderFields {
  LATEST
  DUE
}

input CreateTaskInput {
  title: String!
  notes: String
  completed: Boolean
  due: Time
}

input UpdateTaskInput {
  taskID: ID!
  title: String
  notes: String
  completed: Boolean
  due: Time
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type PageInfo {
  endCursor: String!
  hasNextPage: Boolean!
}

interface Connection {
  pageInfo: PageInfo!
  edges: [Edge]!
}
interface Edge {
  cursor: String!
  node: Node!
}
interface Node {
  id: ID!
}

input PaginationInput {
  first: Int
  after: String
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Details will be explained on each implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project settings
&lt;/h2&gt;

&lt;h4&gt;
  
  
  1. Create Go environment using &lt;code&gt;Docker&lt;/code&gt; and &lt;code&gt;docker-compose&lt;/code&gt;.
&lt;/h4&gt;

&lt;p&gt;Create Go environment by go modules and install following softwares or libraries.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go 1.13.4&lt;/li&gt;
&lt;li&gt;Docker, docker-compose&lt;/li&gt;
&lt;li&gt;MySQL 8.0.13&lt;/li&gt;
&lt;li&gt;ORM

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/jinzhu/gorm"&gt;jinzhu/gorm&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;HTTP server

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/labstack/echo"&gt;labstack/echo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;DB migration tool

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/golang-migrate/migrate"&gt;golang-migrate/migrate&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;hot reload for server

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/oxequa/realize"&gt;oxequa/realize&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;struct validation

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/go-playground/validator"&gt;go-playground/validator&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;unique id generator

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/teris-io/shortid"&gt;teris-io/shortid&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; golang:1.13.4-alpine3.10 as build&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apk update &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    git &lt;span class="se"&gt;\
&lt;/span&gt;    gcc &lt;span class="se"&gt;\
&lt;/span&gt;    musl-dev

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; go.mod .&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; go.sum .&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;go mod download

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;linux &lt;span class="nv"&gt;GOARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;amd64 go build &lt;span class="nt"&gt;-o&lt;/span&gt; app main.go

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;GO111MODULE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;off go get github.com/oxequa/realize
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;GO111MODULE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;off go get &lt;span class="nt"&gt;-tags&lt;/span&gt; &lt;span class="s1"&gt;'mysql'&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; github.com/golang-migrate/migrate/cmd/migrate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
version: '3.7'

services:
  app:
    container_name: graphql-app-backend
    build:
      context: ./app
      target: build
    volumes:
      - ./app:/app
    environment:
      DB_HOST: db
      DB_PORT: 3306
      DB_USER: root
      DB_PASSWORD: root
      DB_NAME: graphql-app-development
    ports:
      - 3000:3000
    depends_on:
      - db
    links:
      - db
    tty: true

  db:
    container_name: graphql-app-db
    image: mysql:8.0.13
    volumes:
      - ./db/mysql/data:/var/lib/mysql
      - ./db/mysql/my.cnf:/etc/mysql/conf.d/my.cnf
    environment:
      MYSQL_USER: root
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: graphql-app-development
    ports:
      - 3306:3306
    tty: true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Setup Go modules and create simple HTTP server using echo
&lt;/h4&gt;

&lt;p&gt;Create simple HTTP server with Go using echo. &lt;a href="https://github.com/labstack/echo"&gt;echo&lt;/a&gt; is High performance and minimalist Go web framework.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
    "net/http"

    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"
)

func main() {
    e := echo.New()

    e.Use(middleware.Recover())
    e.Use(middleware.Logger())
    e.Use(middleware.Gzip())

    e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
        AllowOrigins: []string{os.Getenv("CORS_ALLOW_ORIGIN")},
        AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept},
    }))

    e.GET("/health", func(c echo.Context) error {
        return c.NoContent(http.StatusOK)
    })

    e.HideBanner = true
    e.Logger.Fatal(e.Start(":3000"))
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Setup hot reload
&lt;/h4&gt;

&lt;p&gt;Add &lt;code&gt;realize.yml&lt;/code&gt; for setting up hot reload using &lt;a href="https://github.com/oxequa/realize"&gt;realize&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
settings:
  legacy:
    force: false
    interval: 0s

schema:
  - name: app
    path: .
    commands:
      install:
        status: true
        method: go build -o app main.go
      run:
        status: true
        method: ./app
    watcher:
      extensions:
        - go
      paths:
        - /
      ignored_paths:
        - .realize
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  4. Create Makefile
&lt;/h4&gt;

&lt;p&gt;Create Makefile for running each commands easily.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nv"&gt;DB_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;db
&lt;span class="nv"&gt;DB_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3306
&lt;span class="nv"&gt;DB_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;root
&lt;span class="nv"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;root
&lt;span class="nv"&gt;DB_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;graphql-app-development
&lt;span class="nv"&gt;DB_CONN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mysql://&lt;span class="nv"&gt;${DB_USER}&lt;/span&gt;:&lt;span class="nv"&gt;${DB_PASSWORD}&lt;/span&gt;@tcp&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="nv"&gt;${DB_HOST}&lt;/span&gt;:&lt;span class="nv"&gt;${DB_PORT}&lt;/span&gt;&lt;span class="se"&gt;\)&lt;/span&gt;/&lt;span class="nv"&gt;${DB_NAME}&lt;/span&gt;

&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;
&lt;span class="nl"&gt;run&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker-compose up &lt;span class="nt"&gt;--build&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;
&lt;span class="nl"&gt;start&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker-compose &lt;span class="nb"&gt;exec &lt;/span&gt;app realize start &lt;span class="nt"&gt;--run&lt;/span&gt;

&lt;span class="c"&gt;# create migration file
&lt;/span&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;migrate-create&lt;/span&gt;
&lt;span class="nl"&gt;migrate-create&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker-compose &lt;span class="nb"&gt;exec &lt;/span&gt;app migrate create &lt;span class="nt"&gt;-ext&lt;/span&gt; sql &lt;span class="nt"&gt;-dir&lt;/span&gt; migrations &lt;span class="nv"&gt;${FILENAME}&lt;/span&gt;

&lt;span class="c"&gt;# run migration
&lt;/span&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;migrate-up&lt;/span&gt;
&lt;span class="nl"&gt;migrate-up&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker-compose &lt;span class="nb"&gt;exec &lt;/span&gt;app migrate &lt;span class="nt"&gt;--source&lt;/span&gt; file://migrations &lt;span class="nt"&gt;--database&lt;/span&gt; &lt;span class="nv"&gt;${DB_CONN}&lt;/span&gt; up

&lt;span class="c"&gt;# run migration(rollback)
&lt;/span&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;migrate-down&lt;/span&gt;
&lt;span class="nl"&gt;migrate-down&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker-compose &lt;span class="nb"&gt;exec &lt;/span&gt;app migrate &lt;span class="nt"&gt;--source&lt;/span&gt; file://migrations &lt;span class="nt"&gt;--database&lt;/span&gt; &lt;span class="nv"&gt;${DB_CONN}&lt;/span&gt; down 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  5. Overview
&lt;/h4&gt;

&lt;p&gt;File structure should be like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tree backend
&lt;span class="nb"&gt;.&lt;/span&gt;
├── .gitignore
├── Makefile
├── README.md
├── app
│   ├── .realize.yaml
│   ├── Dockerfile
│   ├── go.mod
│   ├── go.sum
│   └── main.go
├── db
│   └── mysql
│       └── my.cnf
└── docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When you run &lt;code&gt;make&lt;/code&gt; at &lt;code&gt;backend&lt;/code&gt; directory, &lt;code&gt;app&lt;/code&gt; service and &lt;code&gt;db&lt;/code&gt; server will be up. And you can start server with &lt;code&gt;make start&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;make start
docker-compose &lt;span class="nb"&gt;exec &lt;/span&gt;app realize start &lt;span class="nt"&gt;--run&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;14:28:00][APP] : Watching 9 file/s 6 folder/s
&lt;span class="o"&gt;[&lt;/span&gt;14:28:00][APP] : Install started
&lt;span class="o"&gt;[&lt;/span&gt;14:28:01][APP] : Install completed &lt;span class="k"&gt;in &lt;/span&gt;0.748 s
&lt;span class="o"&gt;[&lt;/span&gt;14:28:01][APP] : Running..
&lt;span class="o"&gt;[&lt;/span&gt;14:28:02][APP] : ⇨ http server started on &lt;span class="o"&gt;[&lt;/span&gt;::]:3000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Create tasks table
&lt;/h2&gt;

&lt;h4&gt;
  
  
  1. create and run migration fil
&lt;/h4&gt;

&lt;p&gt;Next, create tasks table.&lt;br&gt;
Run &lt;code&gt;make migrate-create&lt;/code&gt; for creating migration file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ FILENAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;create_tasks make migrate-create
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;&amp;lt;timestamp&amp;gt;_create_tasks.up.sql&lt;/code&gt; and &lt;code&gt;&amp;lt;timestamp&amp;gt;_create_tasks.down.sql&lt;/code&gt; will be created under &lt;code&gt;migrations&lt;/code&gt; directory. So write up/down SQL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE tasks (
  id         INT NOT NULL AUTO_INCREMENT,
  identifier varchar(255) BINARY NOT NULL,
  title      varchar(255) NOT NULL,
  notes      text NOT NULL,
  completed  tinyint(1) NOT NULL DEFAULT 0,
  due        timestamp NULL DEFAULT NULL,
  created_at timestamp NOT NULL,
  updated_at timestamp NOT NULL,
  deleted_at timestamp NULL DEFAULT NULL,
  PRIMARY KEY (id),
  UNIQUE KEY uix_tasks_identifier (identifier)
) ENGINE=InnoDB;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DROP TABLE IF EXISTS tasks;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;migrate-up&lt;/code&gt; for creating table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;make migrate-up
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Table will be like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose &lt;span class="nb"&gt;exec &lt;/span&gt;db &lt;span class="nt"&gt;-it&lt;/span&gt; sh
sh&amp;gt; mysql &lt;span class="nt"&gt;-uroot&lt;/span&gt; &lt;span class="nt"&gt;-proot&lt;/span&gt; graphql-app-development
mysql&amp;gt; desc tasks&lt;span class="p"&gt;;&lt;/span&gt;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| &lt;span class="nb"&gt;id&lt;/span&gt;         | int&lt;span class="o"&gt;(&lt;/span&gt;11&lt;span class="o"&gt;)&lt;/span&gt;      | NO   | PRI | NULL    | auto_increment |
| identifier | varchar&lt;span class="o"&gt;(&lt;/span&gt;255&lt;span class="o"&gt;)&lt;/span&gt; | NO   | UNI | NULL    |                |
| title      | varchar&lt;span class="o"&gt;(&lt;/span&gt;255&lt;span class="o"&gt;)&lt;/span&gt; | NO   |     | NULL    |                |
| notes      | text         | NO   |     | NULL    |                |
| completed  | tinyint&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;   | NO   |     | 0       |                |
| due        | timestamp    | YES  |     | NULL    |                |
| created_at | timestamp    | NO   |     | NULL    |                |
| updated_at | timestamp    | NO   |     | NULL    |                |
| deleted_at | timestamp    | YES  |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+
9 rows &lt;span class="k"&gt;in &lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;0.02 sec&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Set up gqlgen
&lt;/h2&gt;

&lt;p&gt;You will set up gqlgen. This step is almost the same as official tutorial.&lt;br&gt;
&lt;a href="https://gqlgen.com/getting-started/"&gt;gqlgen getting started&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Create project template
&lt;/h4&gt;

&lt;p&gt;Run &lt;code&gt;gqlgen init&lt;/code&gt; for creating gqlgen project template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose &lt;span class="nb"&gt;exec &lt;/span&gt;app go run github.com/99designs/gqlgen init
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then following files will be created.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;gqlgen.yml

&lt;ul&gt;
&lt;li&gt;settings for gqlgen&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;generated.go

&lt;ul&gt;
&lt;li&gt;GraphQL runtime (updated by &lt;code&gt;go generate&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;models_gen.go

&lt;ul&gt;
&lt;li&gt;missing model definitions like GraphQL type, input, enum. (updated by &lt;code&gt;go generate&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;resolver.go

&lt;ul&gt;
&lt;li&gt;GraphQL resolver (you will implement business logic for query/mutation)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;schema.graphql

&lt;ul&gt;
&lt;li&gt;schema definitions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;server/server.go

&lt;ul&gt;
&lt;li&gt;run server&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then edit following files.&lt;br&gt;
&lt;code&gt;generated.go&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;move to &lt;code&gt;resolver/generated.go&lt;/code&gt; and rename package&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;models_gen.go&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;move to &lt;code&gt;model/models_gen.go&lt;/code&gt; and rename package&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Update gqlgen settings for applying new file structures.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
schema:
  - "schema/*.graphql"

exec:
  filename: resolver/generated.go
  package: resolver

model:
  filename: model/models_gen.go
  package: model

resolver:
  filename: resolver/resolver.go
  type: Resolver
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Move to &lt;code&gt;resolver/resolver.go&lt;/code&gt; and change content as following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//go:generate go run github.com/99designs/gqlgen

package resolver

type Resolver struct{}

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

func New() *Resolver {
    return &amp;amp;Resolver{}
}

func (r *Resolver) Mutation() MutationResolver {
    return &amp;amp;mutationResolver{r}
}

func (r *Resolver) Query() QueryResolver {
    return &amp;amp;queryResolver{r}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Remove unnecessary types, queries and mutations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type Query {}

type Mutation {}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Delete &lt;code&gt;server/server.go&lt;/code&gt; and add &lt;code&gt;/graphql&lt;/code&gt; endpoint at &lt;code&gt;main.go&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
    "app/resolver"
    "net/http"

    "github.com/99designs/gqlgen/handler"
    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"
)

func main() {
    e := echo.New()

    e.Use(middleware.Recover())
    e.Use(middleware.Logger())
    e.Use(middleware.Gzip())

    e.GET("/health", func(c echo.Context) error {
        return c.NoContent(http.StatusOK)
    })

    e.POST("/graphql", func(c echo.Context) error {
        config := resolver.Config{
          Resolvers: resolver.New(),
        }
        h := handler.GraphQL(resolver.NewExecutableSchema(config))
        h.ServeHTTP(c.Response(), c.Request())

        return nil
    })

    e.HideBanner = true
    e.Logger.Fatal(e.Start(":3000"))
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finally &lt;code&gt;backend/app&lt;/code&gt; directory will be like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tree app
&lt;span class="nb"&gt;.&lt;/span&gt;
├── .realize.yaml
├── Dockerfile
├── go.mod
├── go.sum
├── gqlgen.yml
├── main.go
├── migrations
│   ├── &amp;lt;timestamp&amp;gt;_create_tasks.down.sql
│   └── &amp;lt;timestamp&amp;gt;_create_tasks.up.sql
├── model
│   └── models_gen.go
├── resolver
│   ├── generated.go
│   └── resolver.go
└── schema
    └── schema.graphql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Add &lt;code&gt;generate&lt;/code&gt; command to &lt;code&gt;Makefile&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="c"&gt;# add
&lt;/span&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;
&lt;span class="nl"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker-compose &lt;span class="nb"&gt;exec &lt;/span&gt;app go generate ./...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;gqlgen settings are all done. You will implement queries and mutations by these steps.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add/edit schema at &lt;code&gt;backend/app/schema/*.graphql&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Generate code by &lt;code&gt;make generate&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Implement resolver for fill interface which is created/updated by step 2.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Create task model
&lt;/h2&gt;

&lt;p&gt;You will setup db connection and create task model.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. setup DB
&lt;/h4&gt;

&lt;p&gt;Add db settings to &lt;code&gt;config/db.go&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package config

import (
    "fmt"
    "os"

    _ "github.com/go-sql-driver/mysql"

    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)

var db *gorm.DB

func InitDB() error {
    conn, err := gorm.Open("mysql", dbsn())
    if err != nil {
        return err
    }

    db = conn.Set("gorm:auto_update", false)

    return nil
}

func dbsn() string {
    return fmt.Sprintf(
        "%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&amp;amp;parseTime=True&amp;amp;loc=Local",
        os.Getenv("DB_USER"),
        os.Getenv("DB_PASSWORD"),
        os.Getenv("DB_HOST"),
        os.Getenv("DB_PORT"),
        os.Getenv("DB_NAME"),
    )
}

func DB() *gorm.DB {
    return db
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
func main() {
    e := echo.New()

    if err := config.InitDB(); err != nil {
        panic(err.Error())
    }
    ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Create task model
&lt;/h4&gt;

&lt;p&gt;Create task model at &lt;code&gt;model/task.go&lt;/code&gt;. And setup validation using GORM's &lt;code&gt;BeforeSave&lt;/code&gt; hook and initialize validator at &lt;code&gt;model/model.go&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package model

import "time"

type Task struct {
    ID         int
    Identifier string `validate:"required,max=255"`
    Title      string `validate:"required,max=255"`
    Notes      string `validate:"max=65535"`
    Completed  bool
    Due        *time.Time
    CreatedAt  time.Time
    UpdatedAt  time.Time
    DeletedAt  *time.Time
}

func (t *Task) BeforeSave() error {
    return validator.Struct(t)
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package model

import (
    v "gopkg.in/go-playground/validator.v9"
)

var validator *v.Validate

func init() {
    validator = v.New()
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Add Task type to GraphQL schema
&lt;/h4&gt;

&lt;p&gt;Create Task type to &lt;code&gt;schema/task.graphql&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type Task {
  id: ID!
  title: String!
  notes: String!
  completed: Boolean!
  due: Time
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Time&lt;/code&gt; type is gqlgen's build-in scalar type. So you should define it.&lt;br&gt;
&lt;a href="https://gqlgen.com/reference/scalars/#time"&gt;https://gqlgen.com/reference/scalars/#time&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 追記
scalar Time
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  4. Connect GraphQL type and Go struct at gqlgen.yml
&lt;/h4&gt;

&lt;p&gt;You can connect GraphQL type and Go struct at &lt;code&gt;gqlgen.yml&lt;/code&gt;. GraphQL's ID type should be globally unique. So you should use &lt;code&gt;identifier&lt;/code&gt; over &lt;code&gt;id&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
models:
  Task:
    model: app/model.Task
    fields:
      id:
        resolver: true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  5. Update resolver
&lt;/h4&gt;

&lt;p&gt;Apply updates on &lt;code&gt;gqlgen.yml&lt;/code&gt; by &lt;code&gt;make generate&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;make generate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then &lt;code&gt;TaskResolver&lt;/code&gt; interface will be created at &lt;code&gt;resolver/generated.go&lt;/code&gt;. So create &lt;code&gt;taskResolver&lt;/code&gt; at &lt;code&gt;resolver/task.go&lt;/code&gt;.&lt;br&gt;
&lt;code&gt;ID(ctx context.Context, obj *model.Task)&lt;/code&gt; method will be used for resolving &lt;code&gt;id&lt;/code&gt; of GraphQL &lt;code&gt;Task&lt;/code&gt; type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package resolver

import (
    "app/model"
    "context"
)

type taskResolver struct{ *Resolver }

func (r *Resolver) Task() TaskResolver {
    return &amp;amp;taskResolver{r}
}

func (r *taskResolver) ID(ctx context.Context, obj *model.Task) (string, error) {
    if obj == nil {
      return "", nil
    }

    return obj.Identifier, nil
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Implement CreateTask mutation
&lt;/h2&gt;

&lt;p&gt;You will implement task creation feature. (&lt;code&gt;CreateTask&lt;/code&gt; mutation)&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Define schema
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
type Mutation {
  createTask(input: CreateTaskInput!): Task! # 追記
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 追記
input CreateTaskInput {
  title: String!
  notes: String
  completed: Boolean
  due: Time
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can define without input like &lt;code&gt;createTask(title: String!, ...): Task!&lt;/code&gt;. But it is easy to read by using input. And you can easily handle request arguments because input struct will be created.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Implement resolver
&lt;/h4&gt;

&lt;p&gt;Run &lt;code&gt;make generate&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;make generate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then &lt;code&gt;createTask&lt;/code&gt; will be added at &lt;code&gt;MutationResolver&lt;/code&gt; interface. So you should create &lt;code&gt;CreateTask&lt;/code&gt; at &lt;code&gt;resolver/resolver.go&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (r *mutationResolver) CreateTask(ctx context.Context, input model.CreateTaskInput) (*model.Task, error) {
    db := config.DB()

    id, err := config.ShortID().Generate()
    if err != nil {
        return &amp;amp;model.Task{}, err
    }

    task := model.Task{
        Identifier: id,
        Title:      input.Title,
        Due:        input.Due,
    }
    if input.Notes != nil {
        task.Notes = *input.Notes
    }
    if input.Completed != nil {
        task.Completed = *input.Completed
    }

    if err := db.Create(&amp;amp;task).Error; err != nil {
        return &amp;amp;model.Task{}, err
    }

    return &amp;amp;task, nil
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  4. Check behaviors
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;createTask&lt;/code&gt; implementation is done. Check behaviors by using clients like &lt;a href="https://github.com/graphql/graphiql"&gt;GraphiQL&lt;/a&gt; or &lt;a href="https://github.com/prisma-labs/graphql-playground"&gt;graphql-playground&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KUglSL-M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143774/292528d4-ff2e-2464-d646-a86a7e86406a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KUglSL-M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143774/292528d4-ff2e-2464-d646-a86a7e86406a.png" alt="createTask mutation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Implement UpdateTask mutation
&lt;/h2&gt;

&lt;p&gt;You will implement &lt;code&gt;UpdateTask&lt;/code&gt; mutation.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Define schema
&lt;/h4&gt;

&lt;p&gt;Define schema same as &lt;code&gt;createTask&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
type Mutation {
  ...
  updateTask(input: UpdateTaskInput!): Task! # 追記
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 追記
input UpdateTaskInput {
  taskID: ID!
  title: String
  notes: String
  completed: Boolean
  due: Time
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  2. implement UpdateTask resolver
&lt;/h4&gt;

&lt;p&gt;Generate code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;make generate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then &lt;code&gt;updateTask&lt;/code&gt; will be added to &lt;code&gt;MutationResolver&lt;/code&gt; interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (r *mutationResolver) UpdateTask(ctx context.Context, input model.UpdateTaskInput) (*model.Task, error) {
    db := config.DB()

    var task model.Task
    if err := db.Where("identifier = ?", input.TaskID).First(&amp;amp;task).Error; err != nil {
        return &amp;amp;model.Task{}, err
    }

    params := map[string]interface{}{}
    if input.Title != nil {
        params["title"] = *input.Title
    }
    if input.Notes != nil {
        params["notes"] = *input.Notes
    }
    if input.Completed != nil {
        params["completed"] = *input.Completed
    }
    if input.Due == nil {
        params["due"] = nil
    } else {
        params["due"] = *input.Due
    }

    if err := db.Model(&amp;amp;task).Updates(params).Error; err != nil {
        return &amp;amp;model.Task{}, err
    }

    return &amp;amp;task, nil
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Check behaviors
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;updateTask&lt;/code&gt; implementation is done. Check behaviors by using clients like &lt;a href="https://github.com/graphql/graphiql"&gt;GraphiQL&lt;/a&gt; or &lt;a href="https://github.com/prisma-labs/graphql-playground"&gt;graphql-playground&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jF8nUDAr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143774/575b2403-6556-e53b-f211-3f9a9762746f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jF8nUDAr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143774/575b2403-6556-e53b-f211-3f9a9762746f.png" alt="updataTask mutation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Implement Tasks query
&lt;/h2&gt;

&lt;h4&gt;
  
  
  1. Overview
&lt;/h4&gt;

&lt;p&gt;You will implement &lt;code&gt;tasks&lt;/code&gt; query with pagination. When implement pagination on GraphQL, relay-style pagination is recommended. So you will implement relay-style pagination.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://facebook.github.io/relay/graphql/connections.htm"&gt;relay-style pagination specification&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Define schema
&lt;/h4&gt;

&lt;p&gt;Define schema.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type Query {
  tasks(input: TasksInput!, orderBy: TaskOrderFields!,  page: PaginationInput!): TaskConnection!
}
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type Task implements Node { # add `implements Node`
...
}
# add
type TaskEdge implements Edge {
  cursor: String!
  node: Task!
}

type TaskConnection implements Connection {
  pageInfo: PageInfo!
  edges: [TaskEdge]!
}

input TasksInput {
  completed: Boolean
}

enum TaskOrderFields {
  LATEST
  DUE
}
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type PageInfo {
  endCursor: String!
  hasNextPage: Boolean!
}

interface Connection {
  pageInfo: PageInfo!
  edges: [Edge]!
}

interface Edge {
  cursor: String!
  node: Node!
}

interface Node {
  id: ID!
}

input PaginationInput {
  first: Int
  after: String
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Generate code
&lt;/h4&gt;

&lt;p&gt;Run &lt;code&gt;make generate&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;make generate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Update task struct
&lt;/h4&gt;

&lt;p&gt;Add IsNode method to task struct for implementing Node.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (Task) IsNode() {} // add
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  4. Implement pagination
&lt;/h4&gt;

&lt;p&gt;You will implement pagination like following steps.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Decode cursor from request and get key.&lt;/li&gt;
&lt;li&gt;Create SQL.&lt;/li&gt;
&lt;li&gt;Execute SQL.&lt;/li&gt;
&lt;li&gt;Create connection from result.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You will implement pagination for ordering by &lt;code&gt;created at&lt;/code&gt; or &lt;code&gt;due&lt;/code&gt;. But you can not order by just targeted column.&lt;br&gt;
You can order tasks by &lt;code&gt;created_at&lt;/code&gt; by using &lt;code&gt;ORDER BY id&lt;/code&gt;. But you can't do for &lt;code&gt;due&lt;/code&gt; because it can be duplicated.&lt;/p&gt;

&lt;p&gt;These are example for implementing pagination by non-unique key.&lt;/p&gt;
&lt;h5&gt;
  
  
  id(unique)
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;cursor

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;task:5&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SQL

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SELECT * FROM tasks WHERE id &amp;gt; 5 ORDER BY id DESC;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;
  
  
  due(non-unique)
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;cursor

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;task:5:created_at:123456&lt;/code&gt; (1234.. is unix timestamp)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SQL

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SELECT * FROM tasks WHERE (UNIX_TIMESTAMP(due) &amp;lt; 123456) OR (UNIX_TIMESTAMP(due) = 123456 AND id &amp;lt; 5) ORDER BY due IS NULL ASC, id ASC;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you can implement resolver.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (r *queryResolver) Tasks(ctx context.Context, input model.TasksInput, orderBy model.TaskOrderFields, page model.PaginationInput) (*model.TaskConnection, error) {
    db := config.DB()

    if input.Completed != nil {
        db = db.Where("completed = ?", *input.Completed)
    }

    var err error

    switch orderBy {
    case model.TaskOrderFieldsLatest:
        db, err = pageDB(db, "id", desc, page)
        if err != nil {
            return &amp;amp;model.TaskConnection{PageInfo: &amp;amp;model.PageInfo{}}, err
        }

        var tasks []*model.Task
        if err := db.Find(&amp;amp;tasks).Error; err != nil {
            return &amp;amp;model.TaskConnection{PageInfo: &amp;amp;model.PageInfo{}}, err
        }

        return convertToConnection(tasks, orderBy, page), nil
    case model.TaskOrderFieldsDue:
        db, err = pageDB(db, "UNIX_TIMESTAMP(due)", asc, page)
        if err != nil {
            return &amp;amp;model.TaskConnection{PageInfo: &amp;amp;model.PageInfo{}}, err
        }

        var tasks []*model.Task
        if err := db.Find(&amp;amp;tasks).Error; err != nil {
            return &amp;amp;model.TaskConnection{PageInfo: &amp;amp;model.PageInfo{}}, err
        }

        return convertToConnection(tasks, orderBy, page), nil
    default:
        return &amp;amp;model.TaskConnection{PageInfo: &amp;amp;model.PageInfo{}}, errors.New("invalid order by")
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package resolver

import (
    "app/model"
    "encoding/base64"
    "errors"
    "fmt"
    "strconv"
    "strings"

    "github.com/jinzhu/gorm"
)

type direction string

var (
    // unused: asc direction = "asc"
    desc direction = "desc"
)

func pageDB(db *gorm.DB, col string, dir direction, page model.PaginationInput) (*gorm.DB, error) {
    var limit int
    if page.First == nil {
        limit = 11
    } else {
        limit = *page.First + 1
    }

    if page.After != nil {
        resource1, resource2, err := decodeCursor(*page.After)
        if err != nil {
            return db, err
        }

        if resource2 != nil {
            switch dir {
            case asc:
                db = db.Where(
                     fmt.Sprintf("(%s &amp;gt; ?) OR (%s = ? AND id &amp;gt; ?)", col, col),
                     resource1.ID,
                     resource1.ID, resource2.ID,
                )
            case desc:
                db = db.Where(
                    fmt.Sprintf("(%s &amp;lt; ?) OR (%s = ? AND id &amp;lt; ?)", col, col),
                    resource1.ID,
                    resource1.ID, resource2.ID,
                )
            }
        } else {
            switch dir {
            case asc:
                db = db.Where(fmt.Sprintf("%s &amp;gt; ?", col), resource1.ID)
            case desc:
                db = db.Where(fmt.Sprintf("%s &amp;lt; ?", col), resource1.ID)
            }
        }
    }

    switch dir {
    case asc:
        db = db.Order(fmt.Sprintf("%s IS NULL ASC, id ASC", col))
    case desc:
        db = db.Order(fmt.Sprintf("%s DESC, id DESC", col))
    }

    return db.Limit(limit), nil
}

type cursorResource struct {
    Name string
    ID   int
}

func createCursor(first cursorResource, second *cursorResource) string {
    var cursor []byte
    if second != nil {
        cursor = []byte(fmt.Sprintf("%s:%d:%s:%d", first.Name, first.ID, second.Name, second.ID))
    } else {
        cursor = []byte(fmt.Sprintf("%s:%d", first.Name, first.ID))
    }

    return base64.StdEncoding.EncodeToString(cursor)
}

func decodeCursor(cursor string) (cursorResource, *cursorResource, error) {
    bytes, err := base64.StdEncoding.DecodeString(cursor)
    if err != nil {
        return cursorResource{}, nil, err
    }

    vals := strings.Split(string(bytes), ":")

    switch len(vals) {
    case 2:
      id, err := strconv.Atoi(vals[1])
      if err != nil {
          return cursorResource{}, nil, errors.New("invalid_cursor")
      }

      return cursorResource{Name: vals[0], ID: id}, nil, nil
    case 4:
      id, err := strconv.Atoi(vals[1])
      if err != nil {
          return cursorResource{}, nil, errors.New("invalid_cursor")
      }

      id2, err := strconv.Atoi(vals[3])
      if err != nil {
          return cursorResource{}, nil, errors.New("invalid_cursor")
      }

      return cursorResource{
          Name: vals[0],
          ID:   id,
      }, &amp;amp;cursorResource{
          Name: vals[2],
          ID:   id2,
      }, nil
    default:
        return cursorResource{}, nil, errors.New("invalid_cursor")
    }
}

func convertToConnection(tasks []*model.Task, orderBy model.TaskOrderFields, page model.PaginationInput) *model.TaskConnection {
    if len(tasks) == 0 {
        return &amp;amp;model.TaskConnection{PageInfo: &amp;amp;model.PageInfo{}}
    }

    pageInfo := model.PageInfo{}
    if page.First != nil {
        if len(tasks) &amp;gt;= *page.First+1 {
            pageInfo.HasNextPage = true
            tasks = tasks[:len(tasks)-1]
        }
    }

    switch orderBy {
    case model.TaskOrderFieldsLatest:
        taskEdges := make([]*model.TaskEdge, len(tasks))

        for i, task := range tasks {
            cursor := createCursor(
                cursorResource{Name: "task", ID: task.ID},
                nil,
            )
            taskEdges[i] = &amp;amp;model.TaskEdge{
                Cursor: cursor,
                Node:   task,
            }
        }

        pageInfo.EndCursor = taskEdges[len(taskEdges)-1].Cursor

        return &amp;amp;model.TaskConnection{PageInfo: &amp;amp;pageInfo, Edges: taskEdges}
    case model.TaskOrderFieldsDue:
        taskEdges := make([]*model.TaskEdge, 0, len(tasks))

        for _, task := range tasks {
          if task.Due == nil {
              pageInfo.HasNextPage = false
              return &amp;amp;model.TaskConnection{PageInfo: &amp;amp;pageInfo, Edges: taskEdges}
          }

          cursor := createCursor(
              cursorResource{Name: "task", ID: int(task.Due.Unix())},
              &amp;amp;cursorResource{Name: "due", ID: task.ID},
          )

          taskEdges = append(taskEdges, &amp;amp;model.TaskEdge{
              Cursor: cursor,
              Node:   task,
          })
        }

        pageInfo.EndCursor = taskEdges[len(taskEdges)-1].Cursor

        return &amp;amp;model.TaskConnection{PageInfo: &amp;amp;pageInfo, Edges: taskEdges}
    }

    return &amp;amp;model.TaskConnection{PageInfo: &amp;amp;model.PageInfo{}}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  5. Check behaviors
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;tasks&lt;/code&gt; query implementation is done. Check behaviors by using clients like &lt;a href="https://github.com/graphql/graphiql"&gt;GraphiQL&lt;/a&gt; or &lt;a href="https://github.com/prisma-labs/graphql-playground"&gt;graphql-playground&lt;/a&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  order by latest
&lt;/h5&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nLBFFxyZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143774/090db507-5bc0-9638-8ac8-5bf27b9feefe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nLBFFxyZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143774/090db507-5bc0-9638-8ac8-5bf27b9feefe.png" alt="tasks order by latest query"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  order by due
&lt;/h5&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tj-21VG4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143774/e59fbcb1-480d-0cdd-5062-802aceaecdb9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tj-21VG4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143774/e59fbcb1-480d-0cdd-5062-802aceaecdb9.png" alt="tasks order by due query"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;You finally finished to implement GraphQL server. I hope you enjoyed schema first development on GraphQL. Implementing pagination is a little bit difficult at the last part. Code generation is one of the good solution.&lt;/p&gt;

</description>
      <category>go</category>
      <category>graphql</category>
    </item>
  </channel>
</rss>
