Huma - Go REST/RPC API Framework
SkillDev toolsHuma - Go REST/RPC API Framework. Use with Echo adapter (humaecho) for OpenAPI 3.1 generation.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Huma - Go REST/RPC API Framework skill
What this skill tells your AI
The instructions your AI receives, as published by getopenpost/openpost in .agents/skills/huma/SKILL.md and read by ahel’s review.
Huma generates OpenAPI 3.1 from Go types with zero annotations beyond struct tags. Use the Echo adapter (humaecho) for this project.
Core Pattern
import (
"github.com/danielgtaylor/huma/v2"
"github.com/danielgtaylor/huma/v2/adapters/humaecho"
)
// Setup in main.go
apiGroup := e.Group("/api/v1")
api := humaecho.NewWithGroup(e, apiGroup, huma.DefaultConfig("API", "1.0.0"))
// Handler registration - auto-generates OpenAPI
huma.Register(api, huma.Operation{
OperationID: "get-user",
Method: http.MethodGet,
Path: "/users/{id}",
Summary: "Get a user",
Tags: []string{"Users"},
Errors: []int{404},
}, func(ctx context.Context, input *GetUserInput) (*GetUserOutput, error) {
// handler logic
})
Input/Output Structs
Input structs use tags for path, query, header, body parameters. Output structs define response shape. Always wrap outputs in explicit Body field to avoid Huma interpreting fields as headers.
type GetUserInput struct {
ID string `path:"id" doc:"User ID"`
Verbose bool `query:"verbose" doc:"Include extra details"`
// Body is optional for GET
}
type GetUserOutput struct {
Body struct {
ID string `json:"id"`
Name string `json:"name"`
}
}
IMPORTANT: Fields named Status on output structs must use Body wrapper, otherwise Huma interprets them as HTTP status codes (must be int). Same for CreatedAt which gets treated as response headers.
Validation Tags
type CreateInput struct {
Body struct {
Name string `json:"name" minLength:"1" maxLength:"100"`
Email string `json:"email" format:"email"`
Age int `json:"age" minimum:"0" maximum:"150"`
Role string `json:"role" enum:"admin,user,guest"`
Tags []string `json:"tags" minItems:"1" uniqueItems:"true"`
}
}
Error Handling
return nil, huma.Error404NotFound("not found")
return nil, huma.Error400BadRequest("bad request", &huma.ErrorDetail{...})
return nil, huma.Error401Unauthorized("unauthorized")
return nil, huma.Error500InternalServerError("internal error")
Middleware
Huma middleware signature: func(ctx huma.Context, next func(huma.Context))
// Per-operation middleware
huma.Register(api, huma.Operation{
Middlewares: huma.Middlewares{authMiddleware},
}, handler)
// Global middleware
api.UseMiddleware(loggingMiddleware)
// Context values
ctx = huma.WithValue(ctx, key, value)
val := ctx.Context().Value(key)
Echo Adapter Notes
humaecho.New(echo, config)- creates API from Echo instancehumaecho.NewWithGroup(echo, group, config)- creates API for a group path- Path conversion: Huma
{param}→ Echo:param(automatic) - Error writing requires API reference: pass
apito middleware factory
OpenAPI Spec
The spec is auto-generated. Access via api.OpenAPI() and serve as JSON:
e.GET("/openapi.json", func(c echo.Context) error {
data, _ := json.Marshal(api.OpenAPI())
return c.Blob(http.StatusOK, "application/json", data)
})
Convenience Methods
huma.Get(api, "/items", handler) // auto-generates operation ID
huma.Post(api, "/items", handler)
huma.Put(api, "/items/{id}", handler)
huma.Delete(api, "/items/{id}", handler)
Gotchas
- Don't return
*models.Modeldirectly if it has fields namedStatus,CreatedAtetc. — use explicit output structs withBodywrapper - Handler methods should take
api huma.APIand callhuma.Registerinternally (method-as-registrar pattern) huma.WriteErr(api, ctx, status, msg)needs the API reference — pass it via closure in middleware factorieshuma.WithValuetakes 3 args:(ctx, key, value), not(ctx, context.Context)
Signals
- GitHub stars
- 138
- Forks
- 17
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
huma- Source
- github.com/getopenpost/openpost