# Fuego: a Go web framework that writes the OpenAPI spec from your code

> A Go API framework built on net/http whose routes derive their OpenAPI documentation from generic handler signatures rather than from comments or YAML, with a plug-in adaptor for existing Gin and Echo servers. The friction points are the pre-1.0 version line and the fact that the Gin and Echo adaptors were only completed in the February 2025 release.

**go-fuego/fuego** — Golang Fuego - Web framework generating OpenAPI 3 spec from source code - Pluggable to existing Gin & Echo APIs

- Repository: https://github.com/go-fuego/fuego
- Website: https://go-fuego.dev/
- Stars: 1,776 · Forks: 129
- Language: Go
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/go-fuego-fuego

## What Fuego generates, and from what

Fuego is a production-ready Go API framework that generates OpenAPI documentation from code. The README is emphatic about the source of that documentation: Fuego automatically generates OpenAPI documentation from code, not from comments nor from YAML files.

That distinction is the whole product. In a conventional setup the OpenAPI document is a separate artefact, either hand-written or generated from annotations, and the two drift apart the moment someone changes a handler without regenerating the spec. In Fuego the handler signature is the spec.

The reason this is possible now is generics. The Why Fuego section argues that Chi, Gin, Fiber and Echo are great frameworks but were designed a long time ago, and that their current API does not allow them to deduce OpenAPI types from signatures, things that are now possible with generics. The framework is described as inspired by Nest and built for Go developers.

The hello world shows the shape:

```go
package main

import "github.com/go-fuego/fuego"

func main() {
	s := fuego.NewServer()

	fuego.Get(s, "/", func(c fuego.ContextNoBody) (string, error) {
		return "Hello, World!", nil
	})

	s.Run()
}
```

The handler takes a `fuego.ContextNoBody` and returns a string and an error. Both halves of that signature are useful: the context type says whether there is a body and what its type is, and the return type is what the OpenAPI response schema is derived from. Register the route with `fuego.Get` and the documentation exists.

## Generics carry the body type and the response schema

The POST example shows what a body route looks like, and how little code it takes:

```go
func myController(c fuego.ContextWithBody[MyInput]) (*MyOutput, error) {
	body, err := c.Body()
	if err != nil {
		return nil, err
	}

	return &MyOutput{Message: "Hello, " + body.Name}, nil
}
```

The input struct carries its own validation tag:

```go
type MyInput struct {
	Name string `json:"name" validate:"required"`
}

type MyOutput struct {
	Message string `json:"message"`
}
```

So `MyInput` gives the request body schema and the validation rule, `MyOutput` gives the response schema, and the route registration ties them together:

```go
fuego.Post(s, "/user/{user}", myController)
```

The README comments that line with the note that Fuego automatically generates OpenAPI documentation for this route. The `{user}` path parameter is written in the same style as the standard library's pattern matching, which is a consequence of the router choice discussed below.

The validation tag is handled by go-playground/validator, and the `go.mod` pins `github.com/go-playground/validator/v10` at v10.30.5, along with `github.com/getkin/kin-openapi` at v0.149.0 for the OpenAPI document itself, `github.com/gorilla/schema` for form decoding, `github.com/golang-jwt/jwt/v5` for tokens, `github.com/google/uuid` and `gopkg.in/yaml.v3`.

## net/http underneath, so your middleware still works

The feature list makes a point of lock-in avoidance: Fuego is 100 percent net/http compatible, it is built on top of net/http, and you can use any http.Handler middleware or handler. The router is based on Go 1.22's net/http, with grouping and middleware support, and Fuego also supports log/slog, context and html/template.

The compatibility example shows what that means in practice:

```go
func main() {
	s := fuego.NewServer()

	// Standard net/http middleware
	fuego.Use(s, func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("X-Hello", "World")
			next.ServeHTTP(w, r)
		})
	})

	// Standard net/http handler with automatic OpenAPI route declaration
	fuego.GetStd(s, "/std", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Hello, World!"))
	})

	s.Run()
}
```

Two different registration functions appear there, and the difference matters. `fuego.Get` takes a typed Fuego handler and derives the OpenAPI entry from the signature. `fuego.GetStd` takes a plain http.HandlerFunc and still declares the route in the OpenAPI document, which is the escape hatch for the handler you already have.

The repository layout backs the net/http claim. The tree contains `net_http_mux.go`, `generic_mux.go` and `engine.go` at the top level, plus `default_middlewares.go`, `middleware/`, `param/`, `option/` and an `internal/` directory, and the `extra/` directory holds the separate adapter modules for other routers.

## The Gin and Echo adapters are the newest and least settled part

Fuego can be plugged to an existing Gin or Echo server to generate OpenAPI documentation, and the release history shows this arriving in two steps.

v0.17.0, published on 2024-12-24, is titled Experimental Gin support. Its notes say Fuego now supports the Gin router, and that for the moment only a subset of Fuego's features is enabled, mostly OpenAPI, with validation and transformation not yet plugged in. The advice in the release is to play with the module and raise issues.

v0.18.0, published on 2025-02-05, is titled Echo+Gin compatibility and many more. The notes say a lot of work was done in January, that Fuego now supports Gin and Echo, and that you can plug and play Fuego with your existing app. Examples are ready for gin and echo, and documentation is available.

So the adapters went from experimental to a stated feature in the space of a month, and the newest published release is v0.18.0.

The tree confirms the shape of the implementation. The `extra/` directory contains separate Go modules, since the Makefile's test path list includes `./extra/fuegogin/...`, `./examples/gin-compat/...`, `./extra/sql/...`, `./extra/sqlite3/...`, `./extra/fuegoecho/...`, `./examples/echo-compat/...`, `./extra/fuegomux/...` and `./examples/mux-compat/...`. The examples directory has fifteen entries, including `gin-compat`, `echo-compat`, `mux-compat`, `acme-tls`, `crud-gorm`, `openapi-generate` and `full-app-gourmet`.

The presence of `extra/fuegomux` and `examples/mux-compat` alongside the Gin and Echo adapters is the interesting detail. The adaptors are not a single compatibility layer, they are a pattern, and the net/http mux is one more router in that family.

## Options, transformations and RFC 9457 errors

The parts of a framework that are usually boilerplate are where Fuego puts most of its code, and the OpenAPI options are the clearest example:

```go
fuego.Post(s, "/", myController,
	option.Description("This route does something..."),
	option.Summary("This is my summary"),
	option.Tags("MyTag"), // A tag is set by default according to the return type (can be deactivated)
	option.Deprecated(),  // Marks the route as deprecated in the OpenAPI spec

	option.Query("name", "Declares a query parameter with default value", param.Default("Carmack")),
	option.Header("Authorization", "Bearer token", param.Required()),
	optionPagination,
	optionCustomBehavior,
)
```

Options compose, so a group can be built once and reused:

```go
var optionPagination = option.Group(
	option.QueryInt("page", "Page number", param.Default(1), param.Example("1st page", 1), param.Example("42nd page", 42)),
	option.QueryInt("perPage", "Number of items per page"),
)
```

The `option` package also lets you reach the route object itself, which is how a custom behaviour is attached.

Transformation and custom validation are done by implementing interfaces rather than by configuring a pipeline. The `fuego.InTransform` and `fuego.OutTransform` interfaces are the mechanism, and the README notes they are also useful for custom validation. The example implements the inbound hook on the input struct itself, so the transformation lives next to the type it applies to:

```go
// Will be called just before returning c.Body()
func (r *MyInput) InTransform(context.Context) error {
	r.Name = strings.ToLower(r.Name)

	if r.Name == "fuego" {
		return errors.New("fuego is not a valid name for this input")
	}

	return nil
}
```

Error handling is centralised and follows the standard RFC 9457 for problem details, and serialization covers JSON, XML and HTML forms from the user-provided structs, or you can do it yourself.

## The Makefile shows what the project considers done

The build tooling is a good map of the project's priorities, because the `ci-full` target chains everything the maintainers care about.

The `ci` target is `lint cover`, so the default checks are linting and tests with coverage. The `ci-full` target adds `dependencies-analyze openapi-check check-all-modules lint-markdown bench`.

Each of those is worth noting. `dependencies-analyze` installs `govulncheck` and runs it over the module, so the dependency tree is scanned for known vulnerabilities as part of a full check. `openapi-check` runs `vacuum lint` over the generated OpenAPI document of the petstore example, which means the generated spec is linted against an OpenAPI linter rather than only compared to a golden file. `check-all-modules` runs a shell script of the same name, and the module list it covers is the long PATHS variable that includes every `extra/` adapter and every compatibility example.

Formatting uses gofumpt with the `-extra` flag, linting uses golangci-lint pinned at v2.7.1, and markdown is checked with markdownlint. The golden files can be regenerated with `go test -update` inside the petstore example.

Benchmarks are a first-class target, `go test -bench ./... -benchmem`, and the README header carries a CodSpeed badge, so the benchmark numbers are tracked over time rather than measured once.

The `go.mod` declares Go 1.26.9 and, interestingly, a `retract` block for v1.0.1 and v1.0.0, the latter commented as published accidentally. That retraction is a small piece of history worth knowing: the project published a 1.0.0 and then took it back, and the versions in use are the 0.x ones.

## Where Fuego is the wrong choice

The first wrong case is a team that needs a stable API. The newest published release is v0.18.0 from 2025-02-05, the last push was on 2026-10-14, and the module has retracted its 1.0.0 as accidentally published. A pre-1.0 library with an active but unversioned branch means your dependency can change under you, and the API surface is still moving.

The second wrong case is an existing Gin or Echo application that wants an OpenAPI document and nothing else. The v0.17.0 release notes are explicit that for the Gin adapter only a subset of Fuego's features was enabled, mostly OpenAPI, with validation and transformation not yet plugged in, so the payoff for a large existing application is the spec and little else.

The third is a team that wants a full framework with a large built-in surface, or one that has standardised on a different router. Fuego is deliberately small and net/http based, which is a strength for composability and a weakness if you wanted a batteries-included middleware ecosystem, a template engine choice, or a job queue. It does offer templating with html/template, a-h/templ and maragudk/gomponents, but the framework itself stays out of the way.

The fourth is a project that cannot accept OpenAPI derived purely from types. The framework's premise is that the schema comes from the Go struct, so a route whose response shape is assembled dynamically, or whose schema is defined by an external contract, fights the design rather than using it.

What Fuego offers in exchange is a genuinely small surface with one idea done thoroughly: the handler signature is the contract, and the documentation cannot drift from it.

## Conclusion

Fuego suits a Go team that wants an OpenAPI document that cannot drift from the implementation, that is comfortable with generics as the source of truth for route types, and that would rather build on net/http than replace it. It does not suit a team that wants a stable 1.0 API, since the newest release is v0.18.0 from 2025-02-05 while the last push was on 2026-10-14, and it does not suit a project that must drop a plug-in into an existing Gin or Echo application and expect the full feature set, because the earlier releases enabled only OpenAPI for those routers. Before adopting it, run `make test` and `make bench` on your own payloads, check whether the `extra/fuegogin` and `extra/fuegoecho` modules are still maintained, and read the RFC 9457 error format in the documentation, since it changes how clients must parse failures.

## FAQ

### How do I create a route in Fuego?

Create a server with `fuego.NewServer()` and register the route with a Fuego function such as `fuego.Get(s, "/", handler)` or `fuego.Post(s, "/user/{user}", myController)`. The handler signature determines the request body type and the response schema, and the OpenAPI entry is generated from it.

### Can I use my existing net/http middleware with Fuego?

Yes. Fuego is built on top of net/http and is described as 100 percent net/http compatible. You register standard middleware with `fuego.Use(s, ...)` and can add plain http.HandlerFunc routes with `fuego.GetStd`, which still declares the route in the OpenAPI document.

### Does Fuego work with an existing Gin or Echo application?

Fuego can be plugged to an existing Gin or Echo server to generate OpenAPI documentation. The v0.18.0 release notes from 2025-02-05 describe both as supported, with examples in the `gin-compat` and `echo-compat` directories and the adapter code in the `extra/` directory.

### How does Fuego handle validation and custom transformations?

Validation uses go-playground/validator with tags on your structs, such as `validate:"required"`. Custom transformation and validation are done by implementing the `fuego.InTransform` and `fuego.OutTransform` interfaces, with the inbound hook called just before `c.Body()` returns.

### Which Go version does Fuego require?

The `go.mod` declares `go 1.26.9`, and the router is based on the Go 1.22 `net/http` pattern matching, with grouping and middleware support.

## Sources

- [go-fuego/fuego on GitHub](https://github.com/go-fuego/fuego)
- [License: MIT](https://github.com/go-fuego/fuego/blob/main/LICENSE)
- [Project website](https://go-fuego.dev/)
- [README](https://github.com/go-fuego/fuego/blob/main/README.md)
- [Releases](https://github.com/go-fuego/fuego/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/go-fuego-fuego
