🦫

【Golang】と【PostgreSQL】で始めるバックエンドAPI開発入門

2024/11/08に公開

Golangとは

Go言語(一般的に「Golang」とも呼ばれます)は、Googleによって開発されたオープンソースのプログラミング言語です。2009年にリリースされ、シンプルさ、高速なコンパイル速度、効率的な並行処理のサポートを特徴としています。

主な特徴:

  • シンプルな文法: 学習が容易で、コードの読みやすさと保守性を高めます。
  • 高速なパフォーマンス: コンパイルされたバイナリは高速に実行され、リソースの効率的な使用を可能にします。
  • 並行処理のサポート: ゴルーチンとチャネルを使用して、シンプルかつ効率的に並行処理を実装できます。
  • 強力な標準ライブラリ: ネットワーク、ファイルシステム、暗号化など、多くの機能を標準でサポートしています。

完成後のイメージ

  • RESTful APIエンドポイント(ユーザー管理)
  • PostgreSQLデータベース
  • pgAdmin(データベース管理ツール)
  • Dockerによる環境のコンテナ化

前提条件

以下のツールをインストールしておく必要があります:

※Goのローカルインストールは不要です(Dockerコンテナ内で実行するため)

プロジェクトのセットアップ

1. プロジェクトの作成

mkdir my-go-api
cd my-go-api

2. 必要なファイルの作成

プロジェクトには以下のファイルが必要です:

my-go-api/
├── docker-compose.yml
├── Dockerfile
├── init.sql
├── .env
├── go.mod
└── main.go

環境構築

1. 環境変数の設定(.env)

# Application
APP_NAME=scholapod

# Database
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=testdb
DB_PORT=5432

# pgAdmin
PGADMIN_DEFAULT_EMAIL=admin@example.com
PGADMIN_DEFAULT_PASSWORD=adminpassword
PGADMIN_PORT=5050

2. Dockerfileの作成

FROM golang:1.21-alpine

WORKDIR /app

# ビルドに必要なパッケージのインストール
RUN apk add --no-cache git

# 依存関係のコピーとダウンロード
COPY go.mod .

# 依存関係の初期化とダウンロード
RUN go mod download
RUN go mod tidy

# ソースコードのコピー
COPY . .

# アプリケーションのビルド
RUN go build -o main .

# アプリケーションの実行
CMD ["./main"]

3. Docker Composeの設定

services:
  app:
    build: .
    ports:
      - "8080:8080"
    depends_on:
      db:
        condition: service_healthy
    environment:
      - DB_HOST=db
      - DB_USER=${POSTGRES_USER}
      - DB_PASSWORD=${POSTGRES_PASSWORD}
      - DB_NAME=${POSTGRES_DB}
      - DB_PORT=${DB_PORT}
    env_file:
      - .env

  db:
    image: postgres:14
    container_name: db_${APP_NAME}
    ports:
      - "5433:5432"
    environment:
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=${POSTGRES_DB}
    env_file:
      - .env
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
      interval: 5s
      timeout: 5s
      retries: 5

  pgadmin:
    image: dpage/pgadmin4
    container_name: pgadmin_${APP_NAME}
    environment:
      - PGADMIN_DEFAULT_EMAIL=${PGADMIN_DEFAULT_EMAIL}
      - PGADMIN_DEFAULT_PASSWORD=${PGADMIN_DEFAULT_PASSWORD}
    env_file:
      - .env
    ports:
      - "${PGADMIN_PORT}:80"
    depends_on:
      - db
    volumes:
      - pgadmin_data:/var/lib/pgadmin

volumes:
  postgres_data:
    name: ${APP_NAME}_postgres_data
  pgadmin_data:
    name: ${APP_NAME}_pgadmin_data

4. データベーステーブルの定義(init.sql)

CREATE TABLE IF NOT EXISTS users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100) NOT NULL UNIQUE
);

APIの実装

1. Go Modulesの初期化(go.mod)

module my-go-api

go 1.21

require (
    github.com/gorilla/mux v1.8.1
    github.com/lib/pq v1.10.9
)

2. メインアプリケーションの実装(main.go)

package main

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"

    _ "github.com/lib/pq"
    "github.com/gorilla/mux"
)

// User モデル
type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

// データベース接続情報
func getDB() (*sql.DB, error) {
    dbHost := os.Getenv("DB_HOST")
    dbUser := os.Getenv("DB_USER")
    dbPassword := os.Getenv("DB_PASSWORD")
    dbName := os.Getenv("DB_NAME")
    dbPort := os.Getenv("DB_PORT")

    connStr := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
        dbHost, dbPort, dbUser, dbPassword, dbName)
    
    return sql.Open("postgres", connStr)
}

// ハンドラー関数
func createUser(w http.ResponseWriter, r *http.Request) {
    var user User
    if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    db, err := getDB()
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    defer db.Close()

    query := `INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id`
    err = db.QueryRow(query, user.Name, user.Email).Scan(&user.ID)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(user)
}

func main() {
    // ルーターの初期化
    r := mux.NewRouter()

    // ルートの設定
    r.HandleFunc("/users", createUser).Methods("POST")

    // サーバーの起動
    log.Printf("Server starting on port 8080...")
    log.Fatal(http.ListenAndServe(":8080", r))
}

動作確認方法

1. システムの起動

docker-compose up --build

2. APIの動作確認

# ユーザー作成のテスト
curl -X POST \
  http://localhost:8080/users \
  -H "Content-Type: application/json" \
  -d '{"name":"山田太郎","email":"yamada@example.com"}'

3. pgAdminでのデータベース確認

  1. ブラウザで http://localhost:5050 にアクセス

  2. ログイン情報:

    • Email: PGADMIN_DEFAULT_EMAIL(.envより)
    • Password: PGADMIN_DEFAULT_PASSWORD(.envより)
  3. 新しいサーバー接続の設定:

    • メニュー: サーバーグループを右クリック → "Register" → "Server..."
    • "General"タブ:
      • Name: Scholapod DB
    • "Connection"タブ:
      • Host: db
      • Port: 5432
      • Database: testdb
      • Username: postgres
      • Password: postgres

4. データベースの直接確認

# データベースコンテナに接続
docker-compose exec db psql -U postgres -d testdb

# よく使うpsqlコマンド:
\dt        -- テーブル一覧の表示
\d users   -- usersテーブルの構造確認
\q         -- psqlの終了

トラブルシューティング

1. ポートが既に使用されている場合

エラーメッセージ: Error response from daemon: Ports are not available

解決方法:

# 使用中のポートを確認
lsof -i :5432  # または該当のポート

# 既存のプロセスを停止
sudo service postgresql stop  # PostgreSQLの場合

2. データベース接続エラー

  1. 環境変数の確認:

    • .envファイルの設定が正しいか
    • docker-compose.ymlでの環境変数の参照が正しいか
  2. ネットワーク設定の確認:

docker network ls
docker network inspect my-go-api_default

3. コンテナの状態確認

# コンテナの状態確認
docker-compose ps

# ログの確認
docker-compose logs app    # アプリケーションのログ
docker-compose logs db     # データベースのログ
docker-compose logs pgadmin # pgAdminのログ

参考リンク

株式会社Xronotech

Discussion