Gin基礎 #1 はじめてのサーバー

読了 7分

このシリーズは、Go(Go言語)でWeb APIを作りたい方のための、7編の入門講座です。Go基礎シリーズで文法を身につけたなら、いよいよその文法で実際のHTTPサーバーを書く段階です。

  • #1 はじめてのサーバー ← 今回の記事
  • #2 ルーティングとハンドラー
  • #3 リクエストのバインディングと検証
  • #4 レスポンス処理 — JSON、ステータスコード、エラー
  • #5 ミドルウェア
  • #6 データベース連携 (GORM)
  • #7 プロジェクト構成とミニREST API

今回の記事では、標準ライブラリ net/http だけでは足りない点を押さえ、Ginをインストールして最初のサーバーを立ち上げるところまで進めます。

Ginとは何ですか? #

Gin はGoで最も広く使われているWebフレームワークです。正確にはHTTPルーターとミドルウェア中心の軽量なフレームワークです。標準ライブラリ net/http の上に薄く乗っているので、Goの基本動作をほぼそのまま保ちながら、繰り返し作業だけを減らしてくれます。

主な特徴は次のとおりです。

  • 高速なルーティング — radix tree ベースのルーターで経路マッチングが速い
  • 簡潔なハンドラーgin.Context 一つでリクエストの読み取りとレスポンスの書き込みを処理
  • ミドルウェアチェーン — ロギング、リカバリ、認証のような共通処理をきれいに連結
  • 便利機能 — JSONバインディング、検証、ファイルレスポンスが標準で提供

Go界隈では事実上の標準に近い選択肢なので、資料やサンプルが最も多いです。入門段階で詰まったときに検索で答えを見つけやすいという点も大きな長所です。

net/httpだけではなぜ足りないのですか? #

Goは標準ライブラリだけでもHTTPサーバーを書けます。Go 1.22からは net/httpServeMux がメソッドと経路パターンマッチングまでサポートします。なので、簡単なサーバーであれば外部フレームワークは全く必要ありません。

ところが実際のAPIを作っていくと、同じコードを繰り返し書くことになります。

net/httpでJSONレスポンス
package main

import (
	"encoding/json"
	"net/http"
)

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("GET /ping", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		json.NewEncoder(w).Encode(map[string]string{"message": "pong"})
	})
	http.ListenAndServe(":8080", mux)
}

JSONを一つ送り出すだけでも、ヘッダー設定、ステータスコード、エンコーディングを毎回自分で書かなければなりません。経路パラメータを読み、リクエストボディを検証し、複数のハンドラーに共通ロジックを掛ける作業まで加わると、コードはすぐ冗長になります。

Ginはまさにこの繰り返しを減らしてくれます。同じレスポンスをGinで書くとこうなります。

Ginで同じレスポンス
r.GET("/ping", func(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{"message": "pong"})
})

c.JSON 一行が、ヘッダー設定、ステータスコード、エンコーディングをまとめて処理します。違いは小さく見えますが、エンドポイントが数十個に増えると、積み重なった違いは大きくなります。

他のGo Webフレームワークとの比較 #

Gin以外にも選択肢があるので、おおよその位置づけだけ押さえておきます。

GinEchoFiberChi
基盤net/httpnet/httpfasthttpnet/http
性格ルーター+ミドルウェアルーター+ミドルウェアExpressスタイル薄いルーター
エコシステム最も大きい大きい大きい標準親和
学習難易度低い低い低い最も低い

EchoはGinとほぼ同じ位置づけで、Fiberは net/http ではなく fasthttp の上に乗って性能をより重視する代わりに、標準互換性を一部諦めています。Chiは標準 net/http に最も忠実な薄いルーターです。入門であれば資料が最も多い Ginで始めて、必要なときに他の選択肢を見てみることをおすすめします。

準備 — Goモジュール #

GinはGoパッケージなので、Goが先にインストールされている必要があります。インストールがまだなら Go基礎 #1 を先に見てきてください。このシリーズは Go 1.22以上 を前提とします。

新しいプロジェクトを作ってモジュールを始めます。

新規プロジェクト
mkdir gin-hello
cd gin-hello
go mod init gin-hello

go mod initgo.mod ファイルを作り、新しいモジュールを始めます。モジュールの概念はGo基礎シリーズで扱ったので、ここではそのまま進めます。

Ginのインストール #

go get でGinを依存関係に追加します。

Ginのインストール
go get github.com/gin-gonic/gin

このシリーズは Gin v1.10基準 で書きます。インストールすると go.mod に依存関係が記録され、go.sum にチェックサムが残ります。どちらもバージョン管理(git)に含めてください。

最初のサーバー — Hello, Gin #

main.go ファイルを作って次のように書きます。

main.go
package main

import (
	"net/http"

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

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

	r.GET("/ping", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"message": "pong"})
	})

	r.Run() // デフォルト :8080
}

実行します。

実行
go run main.go

サーバーが立ち上がったら、別のターミナルからリクエストを送ってみます。

リクエスト
curl http://localhost:8080/ping
# {"message":"pong"}

ブラウザで http://localhost:8080/ping を開いても同じJSONが見えます。最初のGinサーバーが動きました。

コードを一行ずつ読み解く #

main.go 再掲
r := gin.Default()

r.GET("/ping", func(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{"message": "pong"})
})

r.Run()
  • gin.Default() — ルーター(エンジン)を作ります。ロギングとリカバリのミドルウェアがデフォルトで付いています。
  • r.GET("/ping", ...) — GETメソッドで /ping 経路が来たら実行するハンドラーを登録します。
  • func(c *gin.Context) — ハンドラーの形です。すべてのリクエスト情報とレスポンス機能が gin.Context 一つに収められています。
  • c.JSON(...) — ステータスコードとデータを受け取り、JSONでレスポンスします。gin.Hmap[string]any の短縮表記です。
  • r.Run() — サーバーを起動します。引数を空にすると :8080 でlistenします。r.Run(":3000") のようにポートを指定することもできます。

ステータスコードを 200 のような数字ではなく http.StatusOK で書いた点に注目してください。意味が明確になり、タイプミスで誤ったコードを入れる危険が減ります。このシリーズでは標準定数を使う方針で進めます。

gin.Default() vs gin.New() #

ルーターを作る方法は二つあります。

二つの作成方法
r := gin.Default() // Logger + Recovery ミドルウェア込み
r := gin.New()     // ミドルウェアなしの空エンジン

gin.Default() は、リクエストログを出す Logger と、ハンドラーでpanicが起きてもサーバーが落ちないように防ぐ Recovery ミドルウェアを自動で付けます。入門段階では gin.Default() で十分です。

gin.New() は何のミドルウェアもない空のエンジンです。どのミドルウェアを付けるか自分で制御したいときに使います。ミドルウェアは #5 ミドルウェア で詳しく扱います。

リリースモード #

サーバーを初めて立ち上げると、コンソールに次の警告が見えます。

開発モード警告
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.

Ginはデフォルトがdebugモードなので、登録されたルート一覧のようなデバッグ情報を出力します。実際のデプロイ環境ではreleaseモードに切り替えて不要なログを減らします。

リリースモード
gin.SetMode(gin.ReleaseMode)

または環境変数 GIN_MODE=release でも設定されます。今は開発中なので、debugモードのままにしておいても大丈夫です。デプロイ関連の設定は中級シリーズで改めて押さえます。

ホットリロード — 保存すると自動再起動 #

コードを直すたびに go run を実行し直すのは面倒です。air のようなツールを使うと、ファイルが変わるたびに自動でビルドし直してサーバーを再起動してくれます。

airのインストールと実行
go install github.com/air-verse/air@latest
air

必須ではありませんが、開発の快適さが大きく上がります。インストールしなくてもこのシリーズを追っていくのに支障はありません。

まとめ #

今回の記事で整理した内容です。

  • Ginは net/http の上に薄く乗った、Goで最も広く使われているWebフレームワーク
  • 標準ライブラリだけでもサーバーは作れるが、JSONレスポンス、検証、共通処理の繰り返しをGinが減らしてくれる
  • インストールは go get github.com/gin-gonic/gin、シリーズはGin v1.10基準
  • gin.Default() でルーターを作り、r.GET でハンドラーを登録、r.Run() で起動
  • レスポンスは c.JSON(http.StatusOK, gin.H{...}) 一行
  • ステータスコードは http.StatusOK のような標準定数で
  • 開発中のホットリロードはairが便利

次の記事(#2 ルーティングとハンドラー)では、経路パラメータとクエリストリングを読む方法、ルーターグループでエンドポイントをまとめる方法を整理します。

X