Skip to main content
The App struct is the core of a Fuego application. It manages routing, middleware, server lifecycle, and configuration.

Creating an App

import "github.com/abdul-hamid-achik/fuego/pkg/fuego"

// Basic app with defaults
app := fuego.New()

// App with options
app := fuego.New(
    fuego.WithAppDir("routes"),
    fuego.WithStaticDir("public"),
)
When using file-based routing (the default), you typically don’t need to manually register routes. The app.Scan() and app.Mount() methods handle this automatically.

App Methods

Routing Methods

Register HTTP route handlers for different methods.

Get

app.Get(pattern string, handler HandlerFunc)
Register a GET route handler.
app.Get("/users", func(c *fuego.Context) error {
    return c.JSON(200, users)
})

Post

app.Post(pattern string, handler HandlerFunc)
Register a POST route handler.
app.Post("/users", func(c *fuego.Context) error {
    var user User
    if err := c.Bind(&user); err != nil {
        return fuego.BadRequest("invalid body")
    }
    return c.JSON(201, user)
})

Put

app.Put(pattern string, handler HandlerFunc)
Register a PUT route handler for full resource updates.

Patch

app.Patch(pattern string, handler HandlerFunc)
Register a PATCH route handler for partial updates.

Delete

app.Delete(pattern string, handler HandlerFunc)
Register a DELETE route handler.
app.Delete("/users/{id}", func(c *fuego.Context) error {
    id := c.Param("id")
    // Delete user...
    return c.NoContent()
})
app.Head(pattern string, handler HandlerFunc)
Register a HEAD route handler.

Options

app.Options(pattern string, handler HandlerFunc)
Register an OPTIONS route handler.

RegisterRoute

app.RegisterRoute(method, pattern string, handler HandlerFunc)
Register a route with any HTTP method.
app.RegisterRoute("CUSTOM", "/webhook", handler)
Add middleware to the application.

Use

app.Use(mw MiddlewareFunc)
Add global middleware that applies to all routes.
app.Use(fuego.Logger())
app.Use(fuego.Recover())
app.Use(fuego.CORS())
Middleware is executed in the order it’s added. Add logging first to capture all requests.

Group

app.Group(pattern string, fn func(g *RouteGroup))
Create a route group with shared middleware and path prefix.
app.Group("/api", func(api *fuego.RouteGroup) {
    api.Use(authMiddleware)
    
    api.Get("/users", listUsers)
    api.Post("/users", createUser)
    
    // Nested group
    api.Group("/admin", func(admin *fuego.RouteGroup) {
        admin.Use(adminOnlyMiddleware)
        admin.Get("/stats", getStats)
    })
})
Serve static files from a directory.

Static

app.Static(path string, dir string)
Serve static files from the specified directory.
// Serve files from ./static at /static/*
app.Static("/static", "static")

// Serve files from ./public at /assets/*
app.Static("/assets", "public")
The static file server does not list directories. Requests to directories without an index file return 404.
Control the HTTP server.

Listen

app.Listen(addr ...string) error
Start the HTTP server. If no address is provided, uses :3000 or the PORT environment variable.
// Default port 3000
app.Listen()

// Custom port
app.Listen(":8080")

// Custom host and port
app.Listen("127.0.0.1:8080")
Listen blocks until the server is stopped. Use Shutdown for graceful shutdown.

Shutdown

app.Shutdown(ctx context.Context) error
Gracefully shutdown the server, waiting for active connections to complete.
// Graceful shutdown with timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

if err := app.Shutdown(ctx); err != nil {
    log.Fatal("Shutdown error:", err)
}

Addr

app.Addr() string
Get the address the server is listening on.
go app.Listen(":0") // Random available port
time.Sleep(100 * time.Millisecond)
fmt.Println("Listening on", app.Addr())

ServeHTTP

app.ServeHTTP(w http.ResponseWriter, r *http.Request)
Implement http.Handler interface. Useful for testing or embedding in other servers.
// Use with http.ListenAndServe
http.ListenAndServe(":8080", app)

// Use with httptest
req := httptest.NewRequest("GET", "/", nil)
rec := httptest.NewRecorder()
app.ServeHTTP(rec, req)
Access and modify app configuration.

Config

app.Config() *Config
Get the application configuration.
cfg := app.Config()
fmt.Println("App directory:", cfg.AppDir)

Router

app.Router() chi.Router
Get the underlying Chi router for advanced routing needs.
router := app.Router()
router.Mount("/legacy", legacyHandler)

RouteTree

app.RouteTree() *RouteTree
Get the scanned route tree (after calling Scan).

Scan

app.Scan() error
Scan the app directory for routes, middleware, pages, and proxy.
if err := app.Scan(); err != nil {
    log.Fatal("Scan error:", err)
}

Mount

app.Mount()
Mount all scanned routes to the router. Call after Scan.
app.Scan()
app.Mount()
app.Listen()
Control the request logger.

SetLogger

app.SetLogger(config RequestLoggerConfig)
Configure the request logger with custom settings.
app.SetLogger(fuego.RequestLoggerConfig{
    Level:         fuego.LogLevelInfo,
    ShowIP:        true,
    ShowUserAgent: true,
    SkipPaths:     []string{"/health", "/metrics"},
})

EnableLogger

app.EnableLogger()
Enable the request logger (enabled by default).

DisableLogger

app.DisableLogger()
Disable the request logger.
if os.Getenv("GO_ENV") == "test" {
    app.DisableLogger()
}
Configure request interception.

SetProxy

app.SetProxy(proxy ProxyFunc, config *ProxyConfig) error
Set a proxy function for request interception.
app.SetProxy(func(c *fuego.Context) (*fuego.ProxyResult, error) {
    if c.Header("X-API-Key") == "" {
        return fuego.ResponseJSON(401, `{"error":"unauthorized"}`), nil
    }
    return fuego.Continue(), nil
}, &fuego.ProxyConfig{
    Matchers: []string{"/api/*"},
    Excludes: []string{"/api/health"},
})

HasProxy

app.HasProxy() bool
Check if a proxy is configured.

RouteGroup

Route groups allow you to organize routes with shared middleware and path prefixes.
type RouteGroup struct {
    router  chi.Router
    pattern string
}

RouteGroup Methods

MethodDescription
g.Use(mw)Add middleware to this group
g.Get(pattern, handler)Register GET route
g.Post(pattern, handler)Register POST route
g.Put(pattern, handler)Register PUT route
g.Patch(pattern, handler)Register PATCH route
g.Delete(pattern, handler)Register DELETE route
g.Group(pattern, fn)Create nested group

Example

app.Group("/api/v1", func(v1 *fuego.RouteGroup) {
    v1.Use(fuego.Logger())
    
    v1.Group("/users", func(users *fuego.RouteGroup) {
        users.Get("/", listUsers)           // GET /api/v1/users
        users.Post("/", createUser)         // POST /api/v1/users
        users.Get("/{id}", getUser)         // GET /api/v1/users/{id}
        users.Put("/{id}", updateUser)      // PUT /api/v1/users/{id}
        users.Delete("/{id}", deleteUser)   // DELETE /api/v1/users/{id}
    })
    
    v1.Group("/posts", func(posts *fuego.RouteGroup) {
        posts.Get("/", listPosts)
        posts.Post("/", createPost)
    })
})

Complete Example

package main

import (
    "context"
    "log"
    "os"
    "os/signal"
    "syscall"
    "time"

    "github.com/abdul-hamid-achik/fuego/pkg/fuego"
)

func main() {
    app := fuego.New(
        fuego.WithAppDir("app"),
        fuego.WithStaticDir("static"),
    )

    // Global middleware
    app.Use(fuego.Logger())
    app.Use(fuego.Recover())
    app.Use(fuego.RequestID())
    app.Use(fuego.CORS())

    // Serve static files
    app.Static("/static", "static")

    // API routes
    app.Group("/api", func(api *fuego.RouteGroup) {
        api.Get("/health", func(c *fuego.Context) error {
            return c.JSON(200, map[string]string{"status": "ok"})
        })
    })

    // Graceful shutdown
    go func() {
        sigChan := make(chan os.Signal, 1)
        signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
        <-sigChan

        ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
        defer cancel()

        if err := app.Shutdown(ctx); err != nil {
            log.Fatal("Shutdown error:", err)
        }
    }()

    // Start server
    log.Println("Starting server on :3000")
    if err := app.Listen(":3000"); err != nil {
        log.Fatal(err)
    }
}

Next Steps