GoのWebフレームワーク「Fuego」を試してみた!
忙しいエンジニア向けフレームワークだと!?!?
今回は、Go 1.22以降の機能をフル活用し、ソースコードからOpenAPI 3の仕様を自動生成してくれるモダンなWebフレームワーク「Fuego」をご紹介します。日本語のまとまった情報がまだ少なかったので、実際にUbuntu環境で他のフレームワーク(Gin, Echo, そして同じくOpenAPIベースのHuma)とのベンチマーク比較も行ってみました!
Fuegoとは?
Fuegoは、忙しいGo開発者のためのWebフレームワークです。公式サイトでは以下のように紹介されています。
Fuego. The framework for busy Go developers.
Fuego automatically generates OpenAPI documentation from code - not from comments nor YAML files!
最大の特徴は、コメントやYAMLファイルからではなく、Goのソースコード(シグネチャや構造体)から直接OpenAPIドキュメントを自動生成してくれる点です。
GitHubのスター数は現在 約1,700 を獲得しており(2026年4月時点)、Go界隈で注目度が高まっています。
主な特徴
公式GitHubリポジトリから、主要な機能を引用・要約します。
- OpenAPI自動生成: コードから直接OpenAPIドキュメントを生成。Swagger UIもデフォルトで組み込まれています。
-
100% net/http互換: 独自路線に走らず標準の
net/httpの上に構築されているため、既存のミドルウェアやハンドラがそのまま使えます。 - シリアライズ/デシリアライズ: JSON, XML, HTMLフォームなどを自動で処理。
-
バリデーション:
go-playground/validatorをベースにしたシンプルで高速なバリデーション。 - エラーハンドリング: RFC 9457に準拠した標準的なエラーハンドリング。
-
コンテンツネゴシエーション:
Acceptヘッダーに基づいて、1つのコントローラーからJSON/XML/HTMLなど複数のレスポンス形式を提供可能。
「GinやEchoも素晴らしいフレームワークですが、設計が古いためジェネリクスを活用した型推論によるOpenAPI生成が難しい」という背景から、モダンなGoの機能を活かして作られたのがFuegoです。
インストールと最初の一歩
インストール
FuegoはGo 1.22以降の net/http のルーティング機能に依存しているため、Go 1.22以上が必要です。
go get github.com/go-fuego/fuego@latest
Hello World
最もシンプルなサーバーの立ち上げ方は以下の通りです。
package main
import (
"github.com/go-fuego/fuego"
)
func main() {
s := fuego.NewServer()
fuego.Get(s, "/", helloWorld)
s.Run() // デフォルトで localhost:9999 で起動
}
func helloWorld(c fuego.ContextNoBody) (string, error) {
return "Hello, World!", nil
}
これだけでサーバーが起動し、http://localhost:9999/swagger/index.html にアクセスすると、自動生成されたSwagger UIが表示されます!感動的ですね。
公式ドキュメントにはこう書かれています。
The core idea of Fuego is to generate the OpenAPI specification automatically, so you don't have to worry about it.
コントローラーの書き方
Fuegoのコントローラーは、Contextを受け取り、レスポンスとエラーを返す関数です。
Contextの種類
| Context型 | 用途 |
|---|---|
fuego.ContextNoBody |
リクエストボディが不要な場合(GET, DELETE等) |
fuego.ContextWithBody[T] |
リクエストボディを受け取る場合(POST, PUT等) |
// リクエストボディなし(GET用)
func getUser(c fuego.ContextNoBody) (User, error) {
id := c.PathParam("id")
return findUser(id)
}
// リクエストボディあり(POST用)
type CreateUserInput struct {
Name string `json:"name"`
Email string `json:"email"`
}
func createUser(c fuego.ContextWithBody[CreateUserInput]) (User, error) {
body, err := c.Body()
if err != nil {
return User{}, err
}
return saveUser(body.Name, body.Email)
}
リクエストボディの自動デシリアライズ
Fuegoは Content-Type ヘッダーに応じて自動的にデシリアライズしてくれます。
# JSONで送信
curl -X POST http://localhost:9999/users \
-d '{"name": "Taro", "email": "taro@example.com"}' \
-H "Content-Type: application/json"
# XMLでも送信できる!
curl -X POST http://localhost:9999/users \
-d '<CreateUserInput><Name>Taro</Name><Email>taro@example.com</Email></CreateUserInput>' \
-H "Content-Type: application/xml"
クエリパラメータ・ヘッダー・Cookie
func myController(c fuego.ContextNoBody) (MyResponse, error) {
// クエリパラメータ
name := c.QueryParam("name")
// ヘッダー
token := c.Header("Authorization")
// Cookie
sessionID := c.Cookie("session_id")
// レスポンスヘッダーのセット
c.SetHeader("X-Custom-Header", "value")
return MyResponse{Name: name}, nil
}
バリデーション
Fuegoは go-playground/validator を使ったバリデーションをサポートしています。構造体タグに validate を付けるだけです。
type User struct {
FirstName string `json:"first_name" validate:"required"`
LastName string `json:"last_name" validate:"required"`
Age int `json:"age" validate:"gte=0,lte=130"`
Email string `json:"email" validate:"email"`
}
また、InTransform メソッドを実装することで、カスタムバリデーションや前処理も可能です。
func (u *User) InTransform(ctx context.Context) error {
u.FirstName = strings.ToUpper(u.FirstName)
u.LastName = strings.TrimSpace(u.LastName)
if u.FirstName == "" {
return errors.New("first name is required")
}
return nil
}
var _ fuego.InTransformer = (*User)(nil) // インターフェース実装チェック
OpenAPI設定まとめ
ルートオプション
エンドポイントごとにOpenAPIのメタデータを定義できます。
import (
"github.com/go-fuego/fuego"
"github.com/go-fuego/fuego/option"
"github.com/go-fuego/fuego/param"
)
fuego.Get(s, "/users", getUser,
option.Summary("ユーザー取得API"),
option.Description("指定されたIDのユーザー情報を取得します"),
option.Tags("Users"),
option.Query("name", "検索するユーザー名",
param.Required(),
param.Default("John"),
param.Example("example 1", "Napoleon"),
),
option.Deprecated(), // 非推奨マークをつける
)
グループオプション
ルートをグループ化して、共通のオプションを設定できます。
// ページネーション用の再利用可能なオプション
var optionPagination = option.Group(
option.QueryInt("page", "ページ番号", param.Default(1)),
option.QueryInt("limit", "1ページあたりの件数", param.Default(10)),
)
api := fuego.Group(s, "/api/v1",
option.Tags("API v1"),
)
fuego.Get(api, "/users", getUsers, optionPagination)
fuego.Post(api, "/users", createUser)
OpenAPI設定項目一覧
WithOpenAPIConfig で細かく制御できます。
| 設定項目 | デフォルト | 説明 |
|---|---|---|
Disabled |
false |
true にするとOpenAPI関連の機能を全て無効化 |
DisableSwaggerUI |
false |
true にするとSwagger UIを無効化 |
DisableLocalSave |
false |
true にするとJSONファイルのローカル保存を無効化 |
DisableMessages |
false |
true にするとサーバー起動時のログメッセージを非表示 |
PrettyFormatJSON |
false |
true にするとOpenAPI JSONを整形して出力 |
SwaggerURL |
/swagger |
Swagger UIのURLパス |
SpecURL |
/swagger/openapi.json |
OpenAPI JSONのURLパス |
JSONFilePath |
doc/openapi.json |
OpenAPI JSONのローカル保存先 |
UIHandler |
Stoplight Elements | カスタムUIハンドラー(Swagger UIなどに変更可能) |
s := fuego.NewServer(
fuego.WithAddr(":8080"),
fuego.WithEngineOptions(
fuego.WithOpenAPIConfig(fuego.OpenAPIConfig{
DisableSwaggerUI: false,
DisableLocalSave: false,
PrettyFormatJSON: true,
SwaggerURL: "/swagger",
SpecURL: "/swagger/openapi.json",
JSONFilePath: "doc/openapi.json",
}),
),
)
OpenAPIスペックをビルド時に生成する
サーバーを起動せずにOpenAPIのJSONファイルだけを生成したい場合は、OutputOpenAPISpec() を使います。
func main() {
s := fuego.NewServer()
fuego.Get(s, "/", helloWorld)
if os.Getenv("GENERATE_OPENAPI") == "true" {
s.OutputOpenAPISpec() // JSONファイルを生成して終了
return
}
s.Run()
}
特定のルートをOpenAPIから隠す
Webページのルートなど、APIドキュメントに載せたくないルートは option.Hide() で非表示にできます。
// Webページ用のグループ(OpenAPIに載せない)
web := fuego.Group(s, "/",
option.Hide(),
)
fuego.Get(web, "/", renderHomePage)
// API用のグループ(OpenAPIに載せる)
api := fuego.Group(s, "/api")
fuego.Get(api, "/users", getUsers)
実際のパフォーマンスはどうなの?(ベンチマーク比較)
「機能がリッチなのは分かったけど、重かったり遅かったりしないの?」と気になりますよね。
そこで、実際のUbuntu環境(Ubuntu 22.04, Go 1.25.0)で、Fuegoと代表的なフレームワーク(Gin, Echo, 標準のnet/http)、そして同じくOpenAPI自動生成を謳う「Huma」を比較してみました。
1. 処理速度(ベンチマーク)
シンプルなJSONを返すHello Worldエンドポイントで go test -bench を実行した結果です(-benchtime=3s -count=1)。
goos: linux
goarch: amd64
pkg: fuego-bench
cpu: Intel(R) Xeon(R) Processor @ 2.50GHz
BenchmarkNetHttp-6 546888 5782 ns/op 6597 B/op 22 allocs/op
BenchmarkGin-6 712880 5214 ns/op 6648 B/op 23 allocs/op
BenchmarkEcho-6 605838 5081 ns/op 6599 B/op 22 allocs/op
BenchmarkHuma-6 515670 7635 ns/op 6442 B/op 26 allocs/op
BenchmarkFuego-6 220081 16339 ns/op 7387 B/op 51 allocs/op
| フレームワーク | 処理時間 (ns/op) | メモリ割り当て (B/op) | アロケーション回数 |
|---|---|---|---|
| Echo | 約 5,081 ns | 6,599 B | 22 回 |
| Gin | 約 5,214 ns | 6,648 B | 23 回 |
| net/http | 約 5,782 ns | 6,597 B | 22 回 |
| Huma | 約 7,635 ns | 6,442 B | 26 回 |
| Fuego | 約 16,339 ns | 7,387 B | 51 回 |
考察:
Fuegoは他の軽量フレームワークと比較すると、約2.5〜3倍ほど処理時間がかかっています。これは、リクエストごとに以下の処理が走るためです。
- リクエストIDの自動生成(UUID)
- slogによる高機能なリクエストロギング
- OpenAPIスペックとの整合性チェック(クエリパラメータの宣言チェックなど)
- バリデーション処理
同じくOpenAPIベースのHumaと比較しても少し重めですが、絶対値で見れば 約16マイクロ秒/リクエスト であり、通常のWebアプリケーションやAPIサーバーとしては十分すぎるほど高速です。
2. バイナリサイズと依存関係
ビルドした実行ファイルのサイズと、go.mod に含まれる依存関係の数も比較しました。
| フレームワーク | バイナリサイズ | 直接の依存パッケージ数 | 全依存パッケージ数 |
|---|---|---|---|
| net/http | 8.0 MB | 0 | 0 |
| Echo | 8.6 MB | 6 | 14 |
| Fuego | 17.0 MB | 8 | 27 |
| Gin | 20.0 MB | 15 | 33 |
(Humaはnet/httpなどのルーターにアダプターとして乗る形式のため、バイナリサイズはルーターに依存します)
考察:
FuegoはOpenAPIのパースや生成のために kin-openapi などのライブラリに依存しているため、標準ライブラリやEchoと比べるとバイナリサイズは大きめ(17MB)です。しかし、Gin(20MB)と比較するとやや小さくまとまっています。
3. OpenAPIベースフレームワークの比較
同じくOpenAPI自動生成を特徴とするHumaとの比較も簡単にまとめます。
| 比較項目 | Fuego | Huma |
|---|---|---|
| スター数(GitHub) | 約1,700 | 約3,900 |
| アーキテクチャ | スタンドアロン(net/http上に構築) | アダプター方式(Gin/Echo/Chi等に対応) |
| 処理速度 | 約16μs/req | 約7.6μs/req |
| バリデーション | go-playground/validator | 独自実装 |
| デフォルトUI | Stoplight Elements | Stoplight Elements |
| Go要件 | 1.22以上 | 1.25以上 |
Humaはアダプター方式なので既存のGin/Echoプロジェクトに後から組み込みやすい、という特徴があります。一方Fuegoは独立したフレームワークとして設計されており、net/httpとの互換性を重視しています。
既存のGin/EchoプロジェクトへのFuego導入
Fuegoは既存のGinやEchoプロジェクトにもアダプターとして組み込めます。公式ドキュメントには「Plug to existing Routers」として専用のページが用意されています。
Ginへの組み込み
import (
"github.com/gin-gonic/gin"
"github.com/go-fuego/fuego"
"github.com/go-fuego/fuego/extra/fuegogin"
)
func main() {
// 1. fuego.NewEngine()でエンジンを初期化
engine := gin.Default()
fuegoEngine := fuego.NewEngine()
// 2. fuegogin.GetGinで既存のGinコントローラーをラップ(既存コードの変更不要)
engine.GET("/old-route", existingGinHandler)
fuegogin.GetGin(fuegoEngine, engine, "/old-route-wrapped", existingGinHandler)
// 3. 導入完了後は完全なFuegoコントローラーに置き換え
fuegogin.Get(fuegoEngine, engine, "/new-route", helloWorld)
engine.Run(":8080")
}
Echoへの組み込み
import (
"github.com/labstack/echo/v4"
"github.com/go-fuego/fuego"
"github.com/go-fuego/fuego/extra/fuegoecho"
)
func main() {
e := echo.New()
fuegoEngine := fuego.NewEngine()
// fuegoecho.GetEchoで既存コントローラーをラップ
fuegoecho.GetEcho(fuegoEngine, e, "/old-route", existingEchoHandler)
// 完全なFuegoコントローラーに置き換え
fuegoecho.Get(fuegoEngine, e, "/new-route", helloWorld)
e.Start(":8080")
}
まとめ
Fuegoは、「パフォーマンスを極限までカリカリにチューニングしたい」という用途よりも、「開発体験を向上させ、APIドキュメントの保守コストをゼロにしたい」というモダンな開発チームに最適なフレームワークです。
| 観点 | 評価 |
|---|---|
| OpenAPI自動生成 | ◎ コードから自動生成、YAML不要 |
| net/http互換性 | ◎ 既存ミドルウェアがそのまま使える |
| 処理速度 | △ 軽量フレームワークより約2.5〜3倍遅い(絶対値は十分) |
| バイナリサイズ | △ 17MB(Echoの約2倍) |
| 学習コスト | ○ net/http互換なので低め |
| バリデーション | ◎ go-playground/validatorで使いやすい |
「ドキュメント書くの面倒だな〜」と思っている方は、ぜひ次のプロジェクトでFuegoの導入を検討してみてはいかがでしょうか?🔥
Discussion