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