【Go言語】Humaで始める効率的なWeb API 開発

## はじめに
今回は、Goの主要なWebフレームワーク、Gin, Fiber, Echo, Chiなどのフレームワークを土台とした、効率よくAPI開発ができる「**Huma**」というフレームワークについてまとめてみました。
本記事では、その魅力をサンプルコードを通じて深掘っていきます！


## Humaとは？
Humaは、OpenAPI 3仕様を駆使してAPIを設計・構築するためのフレームワークです。まるで精巧な地図を手に冒険に出るように、計画的かつ効果的にAPIを構築していくことができます。

この、「**Huma**」というライブラリは最近伸びているらしく、そのスター数は驚異的なスピードで増加しています。


また、HumaはPythonのFastAPIのように、OpenAPI 3ベースのAPIを構築できます。Go言語でFastAPIのような快適な開発体験を求めるなら、Humaが最適な選択肢です。

https://huma.rocks/

https://github.com/danielgtaylor/huma/

### Humaの魅力について
- **シンプルで直感的なAPI設計**
    - エンドポイントの定義やリクエスト/レスポンスのスキーマを、コード内で簡潔に記述することができます。また、Humaは他の有名なGoのWebフレームワーク（例えば、echo）と同様に直感的で使いやすい書き味を提供します

- **OpenAPI 3ベース**
  - OpenAPI 3仕様に準拠したAPIを定義することで、APIの構造化と標準化を促進します
 HumaはPythonのFastAPIに強く影響を受けており、FastAPI同様にYAMLファイルを書くことなく、Goのコードから直接OpenAPIのYAMLを生成できます。
OpenAPIを生成するライブラリとして[swaggo](https://github.com/swaggo/swag)が挙げられますが、swaggoはOpenAPI 2.0までの対応です。
HumaはGoのコードからOpenAPI3.1を生成でき、モダンなAPI開発ができると言えるでしょう。

- **複数のフレームワークに対応**
    - Gin、Fiber、Echo、Chi などのフレームワークをルーターとして利用できます
これは、既存のフレームワークからの移行コストを最小限に抑えるための配慮であり、既存のコードベースに対しても少ない変更で導入が可能です。

## Humaはこんな開発者におすすめ
- **OpenAPI 3.1の対応**
    - 最新のOpenAPI仕様を活用したい場合に最適です

- **既存のフレームワークの活用**
    - chiやnet/httpを既に使用しており、追加の機能が必要な場合に便利です

- **自動ドキュメント生成**
    - コードから自動的にAPIドキュメントを生成したい場合に役立ちます

なお、chiを全面的にHumaに置き換えることは、状況によっては効率的でないかもしれません。しかし、chiとswaggoを使用していて、OpenAPI 2.0の制約に悩んでいる開発者にとって、Humaは強力な解決策となるでしょう。

## 実際に触ってみましょう
まずは、`go get` でインストールしましょう。(Go 1.21以降が必要)
```terminal
$ go get -u github.com/danielgtaylor/huma/v2
```

### 基本的なエンドポイントの作成
以下はシンプルなAPIサーバーの例です。
```golang:main.go
package main

import (
    "context"
    "net/http"

    "github.com/danielgtaylor/huma/v2"
)

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

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

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

    // サーバを起動
    router.ListenAndServe(":8080")
}
```
- `router.Resource("/hello")`：リソースを定義
- `Get("GetHello", "Returns a greeting")`：GETメソッドのエンドポイントを作成
- `router.ListenAndServe(":8080")`：8080番ポートでサーバーを起動

### Humaを使ったAPIサーバーの構築

```golang:main.go
package main

import (
	"context"
	"fmt"
	"net/http"

	"github.com/danielgtaylor/huma/v2"
	"github.com/danielgtaylor/huma/v2/adapters/humachi"
	"github.com/danielgtaylor/huma/v2/humacli"
	"github.com/go-chi/chi/v5"

	_ "github.com/danielgtaylor/huma/v2/formats/cbor"
)

// CLIオプションの定義。`--port`を指定するか、`SERVICE_PORT`環境変数を設定します。
type Options struct {
	Port int `help:"リッスンするポート" short:"p" default:"8888"`
}

// GreetingOutputは挨拶操作のレスポンスを表します。
type GreetingOutput struct {
	Body struct {
		Message string `json:"message" example:"Hello, world!" doc:"挨拶メッセージ"`
	}
}

func main() {
	cli := humacli.New(func(hooks humacli.Hooks, options *Options) {
		router := chi.NewMux()
		api := humachi.New(router, huma.DefaultConfig("My API", "1.0.0"))

		huma.Get(api, "/greeting/{name}", func(ctx context.Context, input *struct {
			Name string `path:"name" maxLength:"30" example:"world" doc:"挨拶する名前"`
		}) (*GreetingOutput, error) {
			resp := &GreetingOutput{}
			resp.Body.Message = fmt.Sprintf("Hello, %s!", input.Name)
			return resp, nil
		})

		hooks.OnStart(func() {
			http.ListenAndServe(fmt.Sprintf(":%d", options.Port), router)
		})
	})

	cli.Run()
}
```
- インポート部分
    - 必要なパッケージをインポートします。特に、humaフレームワークとchiルーターが含まれています。また、CBORフォーマットを有効にするためのインポートもあります

- CLIオプションの定義
    - Options構造体でCLIオプションを定義しています。ここでは、リッスンするポート番号を指定します

- レスポンスの構造体定義
    - GreetingOutput構造体で、挨拶メッセージを返すレスポンスの形式を定義しています

- メイン関数
    - humacli.NewでCLIアプリケーションを作成します
    - chi.NewMuxで新しいルーターを作成し、humachi.NewでHumaのAPIインスタンスを作成します
    - huma.Getで/greeting/{name}エンドポイントを定義し、Hello, {name}!というメッセージを返す処理を定義します

    - hooks.OnStartで、指定されたポートでサーバーを起動します
- 最後に、cli.RunでCLIを実行します


では、サーバーを起動させてプログラムを実行させましょう。
```terminal
$ go run main.go
```

次は、curlコマンドでレスポンスを確認してみましょう。
```terminal
$ curl http://localhost:8888/greeting/John
```
以下のJSONが帰ってきたらOKです！
```json:json
{
"$schema":"http://localhost:8888/schemas/GreetingOutputBody.json",
"message":"Hello, John!"
}
```

### OpenAPIの生成
サーバーを起動すると、`/openapi.json`エンドポイントから自動的にOpenAPIドキュメントが取得できます。
```terminal
curl http://localhost:8080/openapi.json
```
このJSONファイルをSwagger UIなどで読み込むことで、APIドキュメントを自動生成できます。

## まとめ
Humaは、Go言語でのAPI開発を効率化し、モダンな開発体験を提供する強力なフレームワークです。
OpenAPI3仕様を基盤としたAPI開発のサポートや他のフレームワークとの高い互換性など、多くの魅力的な特徴を備えています！

https://pkg.go.dev/github.com/danielgtaylor/huma