👔

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の導入を検討してみてはいかがでしょうか?🔥


参考リンク

VeriCerts Tech Blog

Discussion