爆伸び中のGoライブラリhuma
はじめに
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フレームワークとしての機能を持ちつつ、ルータに関してはchiやnet/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