The framework for busy Go developers
Build your API or web application in minutes!
The only Go framework generating OpenAPI documentation from code. Inspired by Nest, built for Go developers.
Also empowers html/template
, a-h/templ
and maragudk/gomponents
: see the example - actually running in prod!
Chi, Gin, Fiber and Echo are great frameworks. But since they were designed a long time ago, they do not enjoy the possibilities that modern Go provides. Fuego offers a lot of features that make it easy to develop APIs and web applications.
- OpenAPI: Fuego automatically generates OpenAPI documentation from code - not from comments nor yaml files!
net/http
compatible: Fuego is built on top ofnet/http
, so you can use anyhttp.Handler
middleware or handler! Fuego also supportslog/slog
,context
andhtml/template
- Routing: Fuego provides a simple API for the Go 1.22
net/http
- Serialization/Deserialization: Fuego automatically serializes and deserializes JSON, XML and HTML Forms based on user-provided structs (or not, if you want to do it yourself)
- Validation: Fuego provides a simple and fast validator based on
go-playground/validator
- Transformation: easily transform your data by implementing the
fuego.InTransform
andfuego.OutTransform
interfaces - also useful for validation - Middlewares: easily add a custom
net/http
middleware or use the provided middlewares. - Error handling: Fuego provides centralized error handling with the standard RFC 9457 incoming.
- Rendering: Fuego provides a simple and fast rendering system based on
html/template
- you can still also use your own template system liketempl
orgomponents
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()
}
package main
import "github.com/go-fuego/fuego"
type MyInput struct {
Name string `json:"name" validate:"required"`
}
type MyOutput struct {
Message string `json:"message"`
}
func main() {
s := fuego.NewServer()
// Automatically generates OpenAPI documentation for this route
fuego.Post(s, "/", func(c *fuego.ContextWithBody[MyInput]) (MyOutput, error) {
body, err := c.Body()
if err != nil {
return MyOutput{}, err
}
return MyOutput{
Message: "Hello, " + body.Name,
}, nil
})
s.Run()
}
type MyInput struct {
Name string `json:"name" validate:"required"`
}
// 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
}
package main
import "github.com/go-fuego/fuego"
func main() {
s := fuego.NewServer()
// Custom OpenAPI options that cannot be deduced by the controller signature
fuego.Post(s, "/", myController).
WithDescription("This route does something").
WithSummary("This is my summary").
SetTags("MyTag"). // A tag is set by default according to the return type (can be desactivated)
SetDeprecated()
s.Run()
}
All features
package main
import (
"context"
"errors"
"net/http"
"strings"
chiMiddleware "github.com/go-chi/chi/v5/middleware"
"github.com/go-fuego/fuego"
"github.com/rs/cors"
)
type Received struct {
Name string `json:"name" validate:"required"`
}
type MyResponse struct {
Message string `json:"message"`
BestFramework string `json:"best"`
}
func main() {
s := fuego.NewServer(
fuego.WithPort(":8088"),
)
fuego.Use(s, cors.Default().Handler)
fuego.Use(s, chiMiddleware.Compress(5, "text/html", "text/css"))
// Fuego 🔥 handler with automatic OpenAPI generation, validation, (de)serialization and error handling
fuego.Post(s, "/", func(c *fuego.ContextWithBody[Received]) (MyResponse, error) {
data, err := c.Body()
if err != nil {
return MyResponse{}, err
}
c.Response().Header().Set("X-Hello", "World")
return MyResponse{
Message: "Hello, " + data.Name,
BestFramework: "Fuego!",
}, nil
})
// 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()
}
// InTransform will be called when using c.Body().
// It can be used to transform the entity and raise custom errors
func (r *Received) InTransform(context.Context) error {
r.Name = strings.ToLower(r.Name)
if r.Name == "fuego" {
return errors.New("fuego is not a name")
}
return nil
}
// OutTransform will be called before sending data
func (r *MyResponse) OutTransform(context.Context) error {
r.Message = strings.ToUpper(r.Message)
return nil
}
curl http://localhost:8088/std
# Hello, World!
curl http://localhost:8088 -X POST -d '{"name": "Your Name"}' -H 'Content-Type: application/json'
# {"message":"HELLO, YOUR NAME","best":"Fuego!"}
curl http://localhost:8088 -X POST -d '{"name": "Fuego"}' -H 'Content-Type: application/json'
# {"error":"cannot transform request body: cannot transform request body: fuego is not a name"}
net.http.to.Fuego.mov
Views
![image](https://private-user-images.githubusercontent.com/46993939/304231179-394fed17-a1e2-4b67-89b2-8e6c9eeb771b.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjM3MzU0MjEsIm5iZiI6MTcyMzczNTEyMSwicGF0aCI6Ii80Njk5MzkzOS8zMDQyMzExNzktMzk0ZmVkMTctYTFlMi00YjY3LTg5YjItOGU2YzllZWI3NzFiLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA4MTUlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODE1VDE1MTg0MVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTM0MTgyNjFlNTRmY2MyYTMwYmIyZDViOGU4NDBkNDg4MzNiN2Y1ZWJmYTAxOTg4ZTQ2MzAwZjM4MTUxNDg1NDEmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.reMG3jQCxUV3Wq-x4L9J3zhTuc-dllFUHeOLZF1BaPg)
![image](https://private-user-images.githubusercontent.com/46993939/304231321-321088d7-bec4-46cc-a7ee-9a0fa45d7711.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjM3MzU0MjEsIm5iZiI6MTcyMzczNTEyMSwicGF0aCI6Ii80Njk5MzkzOS8zMDQyMzEzMjEtMzIxMDg4ZDctYmVjNC00NmNjLWE3ZWUtOWEwZmE0NWQ3NzExLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA4MTUlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODE1VDE1MTg0MVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTA1NmE3OGM4ZjdlNjhlMDBiNWIwNjU4M2I0MWUzMGMzZjRkYWY1YTJiYTJjN2VmN2MwMmY2OWU4ZDJiNDlkZjAmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.2XjSCR2-fVhyhhHN7aqDAZUhGaniIoQS_t9-5WX3KYM)
![image](https://private-user-images.githubusercontent.com/46993939/304231531-18796a59-b2e4-4e01-81d1-88c581de3466.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjM3MzU0MjEsIm5iZiI6MTcyMzczNTEyMSwicGF0aCI6Ii80Njk5MzkzOS8zMDQyMzE1MzEtMTg3OTZhNTktYjJlNC00ZTAxLTgxZDEtODhjNTgxZGUzNDY2LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA4MTUlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODE1VDE1MTg0MVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTBiMDk4NWZmNTZlMjg4ZjQ4YzYwMzAxOWIxNTcxNzQ5MGNjYmFjODM1ZmE4NWI5ZGJlNzJjMDE1YjQ1MDVmNWYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.g4P_ZBFim1hs0zTTH-zI9z2g8r0tb3NCFhOaIgMoZYs)
- Never forget to return after an error
- OpenAPI schema generated, listing all the routes
- Deserialization and validation are more easy
- Transition to Fuego is easy and fast
See the contributing guide
I know you might prefer to use net/http
directly, but if having a frame can convince my company to use Go instead of Node, I'm happy to use it.