GoとpostgreSQLで簡単なAPIサーバを実装する

概要

Go言語の文法を一通り学習し終えたため、アプリ開発に挑戦しました。今回は、GoのプログラムとpostgreSQLで、簡単なAPIを実装しました。処理の流れとしては、以下のようになります。

f:id:kurobuchimeganex:20210502155005p:plain
実装した処理の流れ

サンプルデータ

postgreSQLには、簡単に学生情報データ(student_name, email, tel)を作成しています。そして、Goの方には全学生の情報を取得するプログラムと、学生の名前を指定して情報を取得するプログラムの2つを用意しています。

f:id:kurobuchimeganex:20210502155931p:plain
サンプルデータ

実装

"net/http"パッケージを使用して、APIサーバーを実装しています。ただ、このパッケージだけだと"/users/ユーザー名"のようなユーザー指定のリクエストに対してうまく処理できないことが分かりました。そこで、"github.com/gorilla/mux"パッケージを使用し、リクエストパスを柔軟に処理できるようにしました。

package main

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

	"github.com/gorilla/mux"

	_ "github.com/lib/pq"
)

// 学生情報
type Student struct {
	Name  string `json:"name"`
	Email string `json:"email"`
	Tel   string `json:"tel"`
}

// エラー情報
type ErrorInfo struct {
	Message string `json:"error_message"`
}

// 学生情報を全取得
func AllStudentInfo(w http.ResponseWriter, r *http.Request) {
	db, err := sql.Open("postgres", "host=db port=5432 user=postgres password=postgres dbname=postgresdb sslmode=disable")

	// クエリの作成・実行
	rows, err := db.Query("SELECT * FROM student")
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	// 実行結果をStudent型に変換
	students := []Student{}
	for rows.Next() {
		var studentname string
		var email string
		var tel string
		if err := rows.Scan(&studentname, &email, &tel); err != nil {
			log.Fatal(err)
		}
		tmpstudent := Student{studentname, email, tel}
		students = append(students, tmpstudent)
	}

	// json形式のデータに変換
	jsonstr, err := json.Marshal(students)
	if err != nil {
		log.Fatal(err)
	}

	// jsonデータを出力
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, string(jsonstr))
}

// 指定の学生情報を取得
func StudentInfo(w http.ResponseWriter, r *http.Request) {
	db, err := sql.Open("postgres", "host=db port=5432 user=postgres password=postgres dbname=postgresdb sslmode=disable")

	// URLからユーザ名を取得
	vars := mux.Vars(r)
	name := vars["name"]

	// クエリの作成・実行
	row := db.QueryRow("SELECT * FROM student WHERE student_name = $1", name)

	// 実行結果をStudent型に変換
	student := Student{}
	if err := row.Scan(&student.Name, &student.Email, &student.Tel); err != nil {
		jsonstr, err := json.Marshal(ErrorInfo{err.Error()})
		if err != nil {
			log.Fatal(err)
		}
		w.Header().Set("Content-Type", "application/json")
		fmt.Fprintf(w, string(jsonstr))
		return
	}

	// json形式のデータに変換
	jsonstr, err := json.Marshal(student)
	if err != nil {
		log.Fatal(err)
	}

	// jsonデータを出力
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, string(jsonstr))

}

func main() {
	// リスナの登録
	r := mux.NewRouter()
	r.HandleFunc("/users/{name}", StudentInfo)
	r.HandleFunc("/", AllStudentInfo)
	http.Handle("/", r)
	http.ListenAndServe(":80", nil)
}

実行結果

実行結果は以下のようになりました。"/"でアクセスした場合、全学生の情報をJSON形式で返しています。"/users/ユーザー名"でアクセスした場合、特定の学生情報のみをJSON形式で返しています。

f:id:kurobuchimeganex:20210502162557p:plain
全学生情報を取得
f:id:kurobuchimeganex:20210502162809p:plain
指定の学生情報を取得

感想

Go言語を学習し始めて間もないですが、文法がシンプルだったり、パッケージが豊富で分かり易かったため、実装しやすかったです。APIサーバーの実装方法を整理できたので、次はフロントエンドの開発に挑戦したいと思います。