3
6

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.

golangで簡単なWebAPIを作成してみた

Posted at

はじめに

今回はgoogle大先生の作成した言語であるgo言語(golang)を使って簡単なWebAPIを作成してみました。

ソースコードはこちら

この記事の対象としてはgo言語で手っ取り早くアプリを作ってみたい人、go言語でデータベースとの通信を行うAPIを作成したい人などを想定しています!

参考にした記事

工数

2h * 3 = 6hくらい?

前提条件

  • os - macOS(Mojave 10.14.6)
  • framework - gin, xo(あとでインストール)
  • migration tool - goose(あとでインストール)
  • sql - mysql

事前準備

必要に応じて読み飛ばしてください

goのインストール

自分はgoのインストールも済んでいなかったのでhomebrewでgoをインストール

$ brew install go

homebrewが入っていない人はhomebrewをまずインストール
(参考: https://qiita.com/pypypyo14/items/4bf3b8bd511b6e93c9f9)

GOPATHの設定

go言語ではgoのライブラリやモジュールのインストール先の親となるディレクトリをGOPATHとして環境変数に設定しなければならないので設定していきます。
~/goに設定するのがオーソドックスなようなので、自分もホームディレクトリ下にgoというディレクトリを作成し、GOPATHに設定しました。

(~/.bash_profile)
...
export GOPATH=/Users/wataru_tajima/go
export PATH=$GOPATH/bin:$PATH

sourceコマンドで変更を反映します。

$ source ~/.bash_profile

プロジェクトの作成

GOPATHという特殊な設定のため、プロジェクトの置き場所に迷う方がいらっしゃるかもしれませんが(自分は迷いました笑)、設定したGOPATH経由でインストールしたモジュールやライブラリをどこからでも参照できるのでプロジェクトの置き場所はどこでも大丈夫です。

プロジェクト構成は以下の通りです。

go_webapi_sample
├── db
│   ├── dbconf.yml
│   └── migrations
│       └── createTask.sql
├── main.go
└── src
    ├── controller
    │   └── task.go
    └── model
        ├── model.go
        └── task.go

手順

DBの作成

$ mysql -u <USER_NAME> -p <PASSWORD>
# DBのユーザー作成過程は省きます

mysql> create database <DB_NAME>;

マイグレーションツールのgooseをインストールし、confファイルをコピーして作成します。

$ go get bitbucket.org/liamstask/goose/cmd/goose

go_webapi_sample $ mkdir db 
go_webapi_sample $ cd db
go_webapi_sample $ cp $GOPATH/src/bitbucket.org/liamstask/goose/db-sample/dbconf.yml ./

dbconf.ymlのdevelopmentの部分のみ以下のように書き換えます。

# db/dbconf.yml
...
development:
    driver: mymysql
    open: tcp:localhost:3306*<DB_NAME>/<USER_NAME>/<PASSWORD>
...

疎通の確認(以下のように表示されればOK)

go_webapi_sample $ goose status
goose: status for environment 'development'
    Applied At                  Migration
    =======================================

taskテーブルを作成します。

go_webapi_sample $ goose create create_task sql

db/migrations/createTask.sqlを以下のように編集します。

# db/migrations/createTask.sql

-- +goose Up
-- SQL in section 'Up' is executed when this migration is applied
CREATE TABLE IF NOT EXISTS task (
    id INT UNSIGNED NOT NULL,
    created_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
    updated_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
    title VARCHAR(255) NOT NULL,
    PRIMARY KEY(id)
);

-- +goose Down
-- SQL section 'Down' is executed when this migration is rolled back
DROP TABLE task;

modelの作成

それぞれのファイルに以下のように記述していきます。


// src/model/model.go

package model

import (
    "database/sql"
    "log"

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

func DBConnect() (db *sql.DB) {
    dbDriver := "mysql"
    dbUser := "go_user"
    dbPass := "wataru"
    dbName := "go_web_app"
    dbOption := "?parseTime=true"
    db, err := sql.Open(dbDriver, dbUser+":"+dbPass+"@/"+dbName+dbOption)
    if err != nil {
        log.Fatal(err)
    }
    return db
}

// src/model/task.go

package model

import (
    "time"
)

type Task struct {
    ID uint `json:"id"`
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
    Title string `json:"title"`
}

controllerの作成

コントローラーについても同様です。

// src/controller/task.go

package controller

import (
    "fmt"
    "net/http"
    "time"

    "../model"
    "github.com/gin-gonic/gin"
)

func TasksGET(c *gin.Context) {
    db := model.DBConnect()
    result, err := db.Query("SELECT * FROM task ORDER BY id DESC")
    if err != nil {
        panic(err.Error())
    }

    tasks := []model.Task{}

    for result.Next() {
        task := model.Task{}
        var id uint
        var createdAt, updatedAt time.Time
        var title string

        err = result.Scan(&id, &createdAt, &updatedAt, &title)
        if err != nil {
            panic(err.Error())
        }

        task.ID = id
        task.CreatedAt = createdAt
        task.UpdatedAt = updatedAt
        task.Title = title
        tasks = append(tasks, task)
    }
    fmt.Println(tasks)
    c.JSON(http.StatusOK, gin.H{"tasks": tasks})
}

今回は読み取り(Read)のみ実装しています。

main.go

// main.go

package main

import (
    "./src/controller"
    "github.com/gin-gonic/gin"
)

func main() {
    router := gin.Default()

    // API namespace
    v1 := router.Group("/api/v1")
    {
        v1.GET("/tasks", controller.TasksGET)
    }

    router.Run(":8080")
}

これでサーバーを立てることによってlocalhost:8080/api/v1/tasksを叩くことでデータベースに格納されたデータ(タスク一覧)を読み取ることができます。

確認

まずはデータベースにデータを格納します。

mysql> use task;
mysql> insert into task (id, title) values
           (1, "todo1"),
           (2, "todo2"),
           (3, "todo3");

# データの確認
mysql> select * from task;
+----+----------------------------+----------------------------+-------+
| id | created_at                 | updated_at                 | title |
+----+----------------------------+----------------------------+-------+
|  1 | 2019-12-26 18:28:57.564010 | 2019-12-26 18:28:57.564010 | todo1 |
|  2 | 2019-12-26 18:29:26.372062 | 2019-12-26 18:29:26.372062 | todo2 |
|  3 | 2019-12-26 18:29:36.072964 | 2019-12-26 18:29:36.072964 | todo3 |
+----+----------------------------+----------------------------+-------+

データが格納されていることが確認できたのでローカルサーバーを建てます。

go_webapi $ go run main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:	export GIN_MODE=release
 - using code:	gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /api/v1/tasks             --> _/Users/wataru_tajima/projects/00_self/go_webapi/src/controller.TasksGET (3 handlers)
[GIN-debug] Listening and serving HTTP on :8080

コマンドラインからcurlコマンドでデータを読み取ります。

$ curl localhost:8080/api/v1/tasks | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   343  100   343    0     0  46960      0 --:--:-- --:--:-- --:--:-- 49000
{
  "tasks": [
    {
      "id": 3,
      "created_at": "2019-12-26T18:29:36.072964Z",
      "updated_at": "2019-12-26T18:29:36.072964Z",
      "title": "todo3"
    },
    {
      "id": 2,
      "created_at": "2019-12-26T18:29:26.372062Z",
      "updated_at": "2019-12-26T18:29:26.372062Z",
      "title": "todo2"
    },
    {
      "id": 1,
      "created_at": "2019-12-26T18:28:57.56401Z",
      "updated_at": "2019-12-26T18:28:57.56401Z",
      "title": "todo1"
    }
  ]
}

正しく読み取ることができました〜

今後の展望

読み取りのみではなく追加、編集、削除、絞りこみ等の処理を実装する
ローカルじゃなくてサーバーにあげてみる

最後までご覧いただきありがとうございます!
また次回の記事でお会いしましょ〜🙌

3
6
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
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?