Help us shape the future of Go! Take the 2026 Go Developer Survey

flare

package module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 30, 2026 License: MIT Imports: 15 Imported by: 0

README

flare

a small, fasthttp-backed router with a gin-like handler, batteries not included.

module: github.com/deglebe/flare

package main

import (
    "log"

    "github.com/deglebe/flare"
)

func main() {
    e := flare.New()
    e.Use(flare.Recover())

    e.GET("/health", func(c *flare.Ctx) {
        c.SetBodyString("ok")
    })
    e.GET("/users/:id", func(c *flare.Ctx) {
        c.JSON(200, map[string]string{"id": c.Param("id")})
    })

    log.Fatal(e.Listen(":8080"))
}

is it any good?

yes

install

go get github.com/deglebe/flare

requires go 1.25.5+ (see go.mod). for faster json encoding/decoding using bytedance's sonic (marginal boost on small apis):

go build -tags flare_sonic

examples

from the repo root:

go run ./examples/basic    # / /health /hello/:name
go run ./examples/api      # token-gated json crud
go run ./examples/static   # get / -> public/index.html
# basic
curl -s localhost:8080/hello/ada

# api (header required)
curl -s -H 'X-Token: secret' localhost:8080/api/users
curl -s -H 'X-Token: secret' -H 'Content-Type: application/json' \
  -d '{"name":"linus"}' localhost:8080/api/users

# static
curl -s localhost:8080/
curl -s localhost:8080/health

how it works

routing

each http method owns a radix tree (:param and *catchall). trailing slashes are strict by default: /a and /a/ are different routes.

# registered GET /users; wrong slash 404s
curl -i localhost:8080/users/   # 404
curl -i localhost:8080/users    # 200

set e.RedirectTrailingSlash = true to redirect the flipped slash instead (301 for GET, 307 otherwise):

curl -i localhost:8080/users/   # 301 Location: .../users

registration freezes on the first Handler()/Listen(). at freeze, flare:

  1. collapses an empty method root that has a single child (reduces hops per request by one)
  2. builds an exact-path table for static leaves so common paths skip walking the full tree.

the seven usual verbs use a fixed method index instead of a map. unknown methods panic at registration.

middleware and Ctx

Use and Group precompose middleware into each route's handler slice when the route is registered, meaning that there is no per-request stack rebuild. Next, Abort/AbortWithStatus/AbortWithStatusJSON, and Recover/RecoverWith follow the usual pattern.

each request borrows a pooled Ctx wrapping *fasthttp.RequestCtx. routes with a single leaf handler call it directly and skip the Next index loop.

requests and responses

helpers stay optional and off the match path:

params/query/headers Param, ParamInt, Query, GetHeader
json JSON, BindJSON (-tags flare_sonic optional)
forms Bind, BindForm
content negotiate Negotiate (html vs json stub)
files Static / StaticFS, File / FileAttachment
redirect Redirect (301/302/307/308; other codes become 302)
client address ClientIP (see below)

Engine.Server is an embedded fasthttp.Server with conservative defaults (4 mib body, 10 s read/write, 60 s idle). override fields before Listen.

NoRoute / NoMethod distinguish unknown paths (404) from wrong methods on a known path (405).

client ip and proxies

by default ClientIP() returns the socket peer and ignores X-Forwarded-For/X-Real-IP. behind nginx or similar, you must allow the proxy hop explicitly:

_ = e.SetTrustedProxies("10.0.0.0/8", "127.0.0.1")
// optional: e.SetRemoteIPHeaders("X-Forwarded-For")

trusted peers may supply those headers; X-Forwarded-For is walked right-to-left, skipping hops that are also trusted.

scope

flare is meant to be a minimal router kernel on top of which to build an equally, if not more performant stack. so, flare is only match, groups, middleware, Ctx, bind/json helpers, static files, and listen/limits, until more is called for in core. sessions, oauth, openapi, orms, heavy validators, and cors/rate-limit packs are intentionally left out, though a companion library may include these features in the future.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrUnsupportedMediaType = errors.New("flare: unsupported media type")

returned by Bind for content-types other than json/form

Functions

func SetJSONCodec

func SetJSONCodec(marshal jsonMarshalFunc, unmarshal jsonUnmarshalFunc)

swaps json encode/decode (nil arg keeps current)

Types

type Accept

type Accept struct {
	JSON any
	HTML string
}

response negotiation set w/ json as default

type Ctx

type Ctx struct {
	*fasthttp.RequestCtx
	// contains filtered or unexported fields
}

pooled request context wrapping *fasthttp.RequestCtx

func (*Ctx) Abort

func (c *Ctx) Abort()

stop the handler stack

func (*Ctx) AbortWithStatus

func (c *Ctx) AbortWithStatus(code int)

set status and abort

func (*Ctx) AbortWithStatusJSON

func (c *Ctx) AbortWithStatusJSON(code int, v any)

write json, then abort

func (*Ctx) Bind

func (c *Ctx) Bind(v any) error

decode body from content-type (json or form-urlencoded) call from handlers only, not from router hot path

func (*Ctx) BindForm

func (c *Ctx) BindForm(v any) error

fill exported string/int fields from post args (form tag, else json, else name) reflect-based, also keep off hot path

func (*Ctx) BindJSON

func (c *Ctx) BindJSON(v any) error

unmarshal post body as json into v

func (*Ctx) ClientIP

func (c *Ctx) ClientIP() string

returns client address if socket peer is listed in SetTrustedProxies, headers from SetRemoteIPHeaders are consulted w/o anything configured headers are ignored and the peer address is returned

func (*Ctx) File added in v0.1.1

func (c *Ctx) File(path string)

File sends a local file as the response body, do not pass user-supplied paths

func (*Ctx) FileAttachment added in v0.1.1

func (c *Ctx) FileAttachment(path, filename string)

FileAttachment sends a local file as a download named filename empty filename uses filepath.Base(path), do not pass user-supplied paths

func (*Ctx) GetHeader

func (c *Ctx) GetHeader(key string) string

request header value, or ""

func (*Ctx) IsAborted

func (c *Ctx) IsAborted() bool

true after Abort / AbortWith*

func (*Ctx) JSON

func (c *Ctx) JSON(code int, v any)

write v as json w/ status; 500 on marshal error

func (*Ctx) Negotiate

func (c *Ctx) Negotiate(code int, a Accept)

writes html when accept prefers it and html is set, otherwise json

func (*Ctx) Next

func (c *Ctx) Next()

run remaining handlers in the stack

func (*Ctx) Param

func (c *Ctx) Param(key string) string

route param by name, or ""

func (*Ctx) ParamInt

func (c *Ctx) ParamInt(key string) (int, error)

Param as int

func (*Ctx) Query

func (c *Ctx) Query(key string) string

query arg value, or ""

func (*Ctx) Redirect added in v0.1.2

func (c *Ctx) Redirect(code int, url string)

Redirect sets Location to url with status, use 301/302/307/308; fasthttp replaces any other code with 302

type Engine

type Engine struct {

	// when true, miss that matches same method with slash flipped redirects
	// (301 GET, 307 otherwise). default false keeps strict /a vs /a/
	RedirectTrailingSlash bool

	// fasthttp limits/timeouts; override before Listen
	Server fasthttp.Server
	// contains filtered or unexported fields
}

fasthttp router trailing slashes are significant by default; see RedirectTrailingSlash

func New

func New() *Engine

new engine w/ conservative server defaults (4mib body, 10s r/w, 60s idle)

func (*Engine) DELETE

func (e *Engine) DELETE(path string, h ...Handler)

func (*Engine) GET

func (e *Engine) GET(path string, h ...Handler)

func (*Engine) Group

func (e *Engine) Group(prefix string, mws ...Handler) *Router

group under prefix with optional middleware

func (*Engine) HEAD

func (e *Engine) HEAD(path string, h ...Handler)

func (*Engine) Handler

func (e *Engine) Handler() fasthttp.RequestHandler

fasthttp handler; freezes routes on first call

func (*Engine) Listen

func (e *Engine) Listen(addr string) error

listen on addr (freezes via Handler)

func (*Engine) NoMethod

func (e *Engine) NoMethod(h ...Handler)

handlers when path exists under another method (405)

func (*Engine) NoRoute

func (e *Engine) NoRoute(h ...Handler)

handlers for unmatched paths (404)

func (*Engine) OPTIONS

func (e *Engine) OPTIONS(path string, h ...Handler)

func (*Engine) PATCH

func (e *Engine) PATCH(path string, h ...Handler)

func (*Engine) POST

func (e *Engine) POST(path string, h ...Handler)

func (*Engine) PUT

func (e *Engine) PUT(path string, h ...Handler)

func (*Engine) SetRemoteIPHeaders

func (e *Engine) SetRemoteIPHeaders(headers ...string)

overrides header list used when peer is trusted defaults restored with nothing passed

func (*Engine) SetTrustedProxies

func (e *Engine) SetTrustedProxies(proxies ...string) error

set cidrs/bare ips allowed to supply forwarded-client headers empty means clientip always returns sock peer

func (*Engine) Static

func (e *Engine) Static(prefix, rootDir string)

serve from rootdir under prefix (get+head) prefix / serves the directory at the site root

func (*Engine) StaticFS added in v0.1.1

func (e *Engine) StaticFS(prefix string, fsys fs.FS)

serve from fsys under prefix (get+head); for embed.FS / os.DirFS

func (*Engine) Use

func (e *Engine) Use(mws ...Handler)

append global middleware; baked into routes at registration

type Handler

type Handler func(*Ctx)

request handler; call Next to continue the stack

func Recover

func Recover() Handler

catch panic, log, respond 500

func RecoverWith

func RecoverWith(fn func(*Ctx, any)) Handler

Recover+panic hook

type Router

type Router struct {
	// contains filtered or unexported fields
}

route group with shared prefix and middleware

func (*Router) DELETE

func (r *Router) DELETE(path string, h ...Handler)

func (*Router) GET

func (r *Router) GET(path string, h ...Handler)

func (*Router) Group

func (r *Router) Group(prefix string, mws ...Handler) *Router

nest group under this prefix

func (*Router) HEAD

func (r *Router) HEAD(path string, h ...Handler)

func (*Router) PATCH

func (r *Router) PATCH(path string, h ...Handler)

func (*Router) POST

func (r *Router) POST(path string, h ...Handler)

func (*Router) PUT

func (r *Router) PUT(path string, h ...Handler)

func (*Router) Static

func (r *Router) Static(prefix, rootDir string)

mount Engine.Static under this group's prefix

func (*Router) StaticFS added in v0.1.1

func (r *Router) StaticFS(prefix string, fsys fs.FS)

mount Engine.StaticFS under this group's prefix

func (*Router) Use

func (r *Router) Use(mws ...Handler)

append group middleware

Directories

Path Synopsis
examples
api command
basic command
static command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL