Skip to main content

Installation

brew install abdul-hamid-achik/tap/fuego-cli

Create a Project

fuego new myapp
cd myapp
This creates:
The route.go in app/api/health/ automatically maps to GET /api/health.

Run Development Server

fuego dev
Visit http://localhost:3000 You’ll see:
  • Hot reload enabled
  • Request logging in your terminal
  • Tailwind CSS watching (if enabled)

Add an API Route

Create app/api/users/route.go:
package users

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

// GET /api/users
func Get(c *fuego.Context) error {
    users := []map[string]any{
        {"id": 1, "name": "Alice"},
        {"id": 2, "name": "Bob"},
    }
    return c.JSON(200, users)
}

// POST /api/users
func Post(c *fuego.Context) error {
    var user struct {
        Name string `json:"name"`
    }
    if err := c.Bind(&user); err != nil {
        return fuego.BadRequest("invalid request body")
    }
    return c.JSON(201, map[string]any{
        "id":   3,
        "name": user.Name,
    })
}
Now you have:
  • GET /api/users - List users
  • POST /api/users - Create user
Handler functions are named after HTTP methods: Get, Post, Put, Patch, Delete, Head, Options.

Test Your Endpoints

# List users
curl http://localhost:3000/api/users

# Create a user
curl -X POST http://localhost:3000/api/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Charlie"}'

Add Middleware

Create app/api/middleware.go:
package api

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

// Middleware applies to all routes under /api/*
func Middleware() fuego.MiddlewareFunc {
    return func(next fuego.HandlerFunc) fuego.HandlerFunc {
        return func(c *fuego.Context) error {
            c.SetHeader("X-API-Version", "1.0")
            return next(c)
        }
    }
}
This middleware automatically applies to all routes under /api/.

Add Dynamic Routes

Create app/api/users/[id]/route.go:
package users

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

// GET /api/users/:id
func Get(c *fuego.Context) error {
    id := c.Param("id")
    return c.JSON(200, map[string]any{
        "id":   id,
        "name": "User " + id,
    })
}

// PUT /api/users/:id
func Put(c *fuego.Context) error {
    id := c.Param("id")
    var input struct {
        Name string `json:"name"`
    }
    if err := c.Bind(&input); err != nil {
        return fuego.BadRequest("invalid request body")
    }
    return c.JSON(200, map[string]any{
        "id":      id,
        "name":    input.Name,
        "updated": true,
    })
}

// DELETE /api/users/:id
func Delete(c *fuego.Context) error {
    id := c.Param("id")
    return c.JSON(200, map[string]string{
        "message": "User " + id + " deleted",
    })
}
Now GET /api/users/123 returns {"id": "123", "name": "User 123"}.

View Your Routes

fuego routes
Output:
  Fuego Routes

  GET     /                             app/page.templ
  GET     /api/health                   app/api/health/route.go
  GET     /api/users                    app/api/users/route.go
  POST    /api/users                    app/api/users/route.go
  GET     /api/users/{id}               app/api/users/[id]/route.go
  PUT     /api/users/{id}               app/api/users/[id]/route.go
  DELETE  /api/users/{id}               app/api/users/[id]/route.go

  Middleware:
  /api/*                                app/api/middleware.go

  Total: 7 routes

Build for Production

fuego build
./myapp
This compiles everything into a single binary with no external dependencies.
The production binary is typically 10-15MB and includes all static files.

Project Structure

Here’s the full structure after adding routes:

Next Steps