🍌

爆伸び中のGoライブラリhuma

2024/12/01に公開

はじめに

2024年に入り、Go言語の世界で急速に注目を集めているWebフレームワーク「huma」をご存知でしょうか?humaはGoでのAPI開発を革新する新しいライブラリで、そのスター数は驚異的なスピードで増加しています。本記事では、humaの魅力とその使い方、そしてサンプルコードを通じてその実力を探っていきます。

humaの良さ

GoからOpenAPI 3.1を生成可能

humaはPythonのFastAPIに強く影響を受けて開発されたライブラリです。FastAPI同様、YAMLファイルを書くことなく、Goのコードから直接OpenAPIのYAMLを生成できます。これにより、APIの設計と実装がシームレスに統合され、開発効率が大幅に向上します。(スキーマファストかコードファストかの議論は別問題)

GoでコードからOpenAPIを生成するライブラリとしてはswaggoが有名ですが、swaggoはOpenAPI 2.0までの対応です。現状、私の知る限り、GoのコードからOpenAPI 3.1を生成できるのはhumaだけです。最新のOpenAPI仕様に対応しているため、モダンなAPI開発が可能となります。

シンプルで直感的なAPI設計

エンドポイントの定義やリクエスト/レスポンスのスキーマをコード内で簡潔に記述できます。また、chiやechoなど他の著名なGoのWebフレームワークとの書き味もとても似ています。

豊富な機能拡張

  • バリデーションの自動化:構造体のタグを用いて入力データのバリデーションが可能。
  • ミドルウェアのサポート:認証やロギングなどのミドルウェアを簡単に統合。
  • 高度なエラーハンドリング:カスタムエラーを定義して一元的に管理。

humaの概要

humaはWebフレームワークとしての機能を持ちつつ、ルータに関してはchinet/httpなど他のフレームワークを組み合わせて使用できます。これは、既存のフレームワークからの移行コストを最小限に抑えるための配慮であり、既存のコードベースに対しても少ない変更で導入が可能です。

humaの使い道

humaは特に以下のような開発者におすすめです:

  • OpenAPI 3.1に対応したい:最新のOpenAPI仕様を活用したい場合。
  • 既存のフレームワークを活かしたい:chiやnet/httpを使っているが、さらなる機能を求めている場合。
  • ドキュメント生成を自動化したい:コードから自動的にAPIドキュメントを生成したい場合。

chiを全面的にhumaに置き換えることは、場合によっては時間の無駄かもしれません。しかし、chiとswaggoを使っていて、OpenAPI 2.0の制約に悩まされている開発者には、humaは強力な解決策となるでしょう。

サンプルコードと解説

環境設定

まず、humaをプロジェクトに導入します:

go get github.com/danielgtaylor/huma

基本的なエンドポイントの作成

以下は、humaを使ったシンプルなAPIサーバの例です。

package main

import (
    "context"
    "net/http"

    "github.com/danielgtaylor/huma"
)

// レスポンス用の構造体を定義
type GreetingResponse struct {
    Message string `json:"message"`
}

func main() {
    // 新しいルータを作成
    router := huma.NewRouter("My API", "1.0.0")

    // GETエンドポイントを定義
    router.Resource("/hello").Get("GetHello", "Returns a greeting").
        Run(func(ctx context.Context, req huma.Request) {
            resp := GreetingResponse{Message: "Hello, World!"}
            req.WriteJSON(http.StatusOK, resp)
        })

    // サーバを起動
    router.ListenAndServe(":8080")
}

解説

  • ルータの作成huma.NewRouterで新しいルータを作成します。引数にはAPIの名前とバージョンを指定します。
  • リソースの定義router.Resource("/hello")でリソースを定義します。
  • エンドポイントの設定Get("GetHello", "Returns a greeting")でGETメソッドのエンドポイントを設定します。引数にはエンドポイントの名前と説明を指定します。
  • ハンドラの実装Run関数内でリクエストを処理し、レスポンスを返します。

リクエストとレスポンスのスキーマ定義

humaでは、リクエストやレスポンスのスキーマを構造体で定義できます。

// リクエスト用の構造体を定義
type UserRequest struct {
    Name string `json:"name" doc:"User's name"`
}

// レスポンス用の構造体を定義
type UserResponse struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

router.Resource("/users").Post("CreateUser", "Creates a new user").
    // リクエストとレスポンスのスキーマを指定
    RequestSchema(UserRequest{}).
    ResponseSchema(http.StatusCreated, "Created", UserResponse{}).
    Run(func(ctx context.Context, req huma.Request) {
        var userReq UserRequest
        err := req.ParseJSONBody(&userReq)
        if err != nil {
            req.WriteError(http.StatusBadRequest, "Invalid request body")
            return
        }

        userResp := UserResponse{ID: 1, Name: userReq.Name}
        req.WriteJSON(http.StatusCreated, userResp)
    })

OpenAPIの生成

サーバを起動すると、/openapi.jsonエンドポイントから自動的にOpenAPIドキュメントが取得できます。

curl http://localhost:8080/openapi.json

このJSONファイルをSwagger UIなどで読み込むことで、APIドキュメントを自動生成できます。

ルータの組み合わせ

既存のchiルータと組み合わせることも可能です。

import (
    "net/http"

    "github.com/go-chi/chi/v5"
    "github.com/danielgtaylor/huma"
)

func main() {
    // 既存のchiルータを作成
    r := chi.NewRouter()

    // humaのルータをchiルータに統合
    router := huma.NewChiRouter(r, "My API", "1.0.0")

    // humaのルートを定義
    router.Resource("/hello").Get("GetHello", "Returns a greeting").
        Run(func(ctx context.Context, req huma.Request) {
            resp := GreetingResponse{Message: "Hello from huma!"}
            req.WriteJSON(http.StatusOK, resp)
        })

    // chiのルートもそのまま使用可能
    r.Get("/chi-endpoint", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("This is a chi endpoint"))
    })

    // サーバを起動
    http.ListenAndServe(":8080", r)
}

ミドルウェアの使用

humaでは、ミドルウェアを簡単に追加できます。

// ロギングミドルウェアを追加
router.Middleware(huma.MiddlewareFunc(func(next huma.Handler) huma.Handler {
    return func(ctx context.Context, req huma.Request) {
        log.Printf("Request: %s %s", req.Method(), req.URL().Path)
        next(ctx, req)
    }
}))

まとめ

humaはGoでのAPI開発をより効率的かつモダンに進めるための強力なツールです。OpenAPI 3.1への対応や既存フレームワークとの互換性など、多くの魅力的な特徴を持っています。

Discussion