SJ blog
backend
S

信頼度ランク

S 公式ソース確認済み
A 成功実績多数・失敗例少数
B 賛否両論
C 動作未確認・セキュリティリスク高
Z 個人所感

GoでゼロからHTTPサーバーを作る

Go標準ライブラリだけでHTTPサーバーを構築する方法を解説。ルーティング・ミドルウェア・JSON API・graceful shutdownまでをサンプルコード付きで紹介します。

一言結論

Go 1.22以降の標準ライブラリはメソッドとパスパラメータ付きルーティングをネイティブサポートし、フレームワークなしでも実用的なHTTP APIサーバーが構築できるレベルに達している。

Go の標準ライブラリで HTTP サーバーを作る

Go 1.22+ から net/http のルーティングが大幅に改善されました。フレームワークなしでも実用的な API サーバーが書けます。

最小限のサーバー

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("GET /hello", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Hello, World!")
    })

    http.ListenAndServe(":8080", nil)
}
go run main.go
curl http://localhost:8080/hello  # → Hello, World!

Go 1.22 の新ルーティング構文

mux := http.NewServeMux()

// メソッド + パスパターンで登録できるように
mux.HandleFunc("GET /users", listUsers)
mux.HandleFunc("POST /users", createUser)
mux.HandleFunc("GET /users/{id}", getUser)    // パスパラメータ
mux.HandleFunc("DELETE /users/{id}", deleteUser)

パスパラメータの取得:

func getUser(w http.ResponseWriter, r *http.Request) {
    id := r.PathValue("id")  // Go 1.22 で追加
    fmt.Fprintf(w, "User ID: %s", id)
}

JSON API を実装する

package main

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

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

func listUsers(w http.ResponseWriter, r *http.Request) {
    users := []User{
        {ID: 1, Name: "Alice"},
        {ID: 2, Name: "Bob"},
    }

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

func createUser(w http.ResponseWriter, r *http.Request) {
    var user User
    if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
        http.Error(w, "Bad Request", http.StatusBadRequest)
        return
    }

    // DB 保存処理(省略)
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(user)
}

ミドルウェアパターン

// ロギングミドルウェア
func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Printf("%s %s", r.Method, r.URL.Path)
        next.ServeHTTP(w, r)
    })
}

// CORS ミドルウェア
func corsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Access-Control-Allow-Origin", "*")
        if r.Method == "OPTIONS" {
            w.WriteHeader(http.StatusNoContent)
            return
        }
        next.ServeHTTP(w, r)
    })
}

// 複数のミドルウェアをチェーン
func chain(h http.Handler, middlewares ...func(http.Handler) http.Handler) http.Handler {
    for i := len(middlewares) - 1; i >= 0; i-- {
        h = middlewares[i](h)
    }
    return h
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("GET /users", listUsers)

    handler := chain(mux, loggingMiddleware, corsMiddleware)
    http.ListenAndServe(":8080", handler)
}

Graceful Shutdown

シグナルを受けてリクエストの処理中断なくシャットダウンします。

package main

import (
    "context"
    "log"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("ok"))
    })

    server := &http.Server{
        Addr:    ":8080",
        Handler: mux,
    }

    // シグナル待受
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

    go func() {
        if err := server.ListenAndServe(); err != http.ErrServerClosed {
            log.Fatal(err)
        }
    }()

    log.Println("Server started on :8080")
    <-quit
    log.Println("Shutting down...")

    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    if err := server.Shutdown(ctx); err != nil {
        log.Fatal("Forced shutdown:", err)
    }
    log.Println("Server stopped")
}

まとめ

Go 1.22+ の標準ライブラリは、シンプルな REST API を作るのに十分な機能を持っています。外部フレームワーク(Gin・Echo・Chi)は高機能ですが、まず標準ライブラリを理解してから選択することを推奨します。

用途推奨
シンプルな API標準ライブラリ
ルーティングが複雑Chi
フル機能フレームワークGin / Echo

参考: Go 公式ドキュメント - net/http / Go 1.22 リリースノート