zentrox

package module
v0.0.0-...-dea8a42 Latest Latest
Warning

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

Go to latest
Published: Oct 27, 2025 License: MIT Imports: 28 Imported by: 0

README

Zentrox

Zentrox

A minimal, fast HTTP framework for Go with simple, clean API.


Quick Start

package main

import (
    "github.com/aminofox/zentrox"
    "github.com/aminofox/zentrox/middleware"
)

func main() {
    app := zentrox.NewApp()

    app.Plug(middleware.Recovery(), middleware.Logger())

    app.GET("/", func(c *zentrox.Context) {
        c.String(200, "Hello!")
    })

    app.GET("/users/:id", func(c *zentrox.Context) {
        c.JSON(200, map[string]string{"id": c.Param("id")})
    })

    app.Run(":8000")
}

Installation

go get github.com/aminofox/zentrox

Features

  • Minimal & Fast - Only essential middleware included
  • Simple API - Clean and easy to learn
  • Easy Integration - Custom logger and JWT support for your existing systems
  • Automatic middleware chaining - No manual c.Next() needed in handlers
  • Fast routing - Compiled trie with path params and wildcards
  • Built-in essentials - CORS, JWT, Gzip, logging, error handling
  • Swagger support - Use swaggo with comment annotations for API documentation
  • Validation & binding - Built-in request validation
  • Context pooling - Zero allocations for high performance

Routing

app.GET("/path", handler)
app.POST("/path", handler)
app.PUT("/path", handler)
app.PATCH("/path", handler)
app.DELETE("/path", handler)
Path Parameters
app.GET("/users/:id", func(c *zentrox.Context) {
    id := c.Param("id")
    c.JSON(200, map[string]string{"id": id})
})
Wildcards
app.GET("/files/*filepath", func(c *zentrox.Context) {
    path := c.Param("filepath")
    c.String(200, "File path: %s", path)
})
Route Groups
api := app.Scope("/api")
api.GET("/users", listUsers)
api.POST("/users", createUser)

Middleware

Global Middleware
app.Plug(
    middleware.Recovery(),
    middleware.Logger(),
    middleware.CORS(middleware.DefaultCORS()),
)
Per-Route Middleware
app.GET("/secure", authMiddleware, handler)
Group Middleware
admin := app.Scope("/admin", authMiddleware)
admin.GET("/stats", statsHandler)

// Or add middleware after creating the group
apiGroup := app.Scope("/api")
apiGroup.Use(authMiddleware)
apiGroup.GET("/users", listUsers)
Custom Middleware
func MyMiddleware() zentrox.Handler {
    return func(c *zentrox.Context) {
        // Before handler
        c.Next() // Call next middleware/handler
        // After handler
    }
}
Built-in Middleware

Zentrox includes only essential middleware for a minimal footprint:

middleware.Recovery()                           // Panic recovery
middleware.Logger()                             // Request logging
middleware.LoggerWithFunc(customLogFn)          // Custom logger integration
middleware.CORS(middleware.DefaultCORS())       // CORS headers
middleware.Gzip()                               // Response compression
middleware.JWT(middleware.DefaultJWT(secret))   // JWT auth
middleware.ErrorHandler(middleware.DefaultErrorHandler()) // Error handling

CORS (Simplified)

app.Plug(middleware.CORS(middleware.CORSConfig{
    AllowOrigins:     []string{"http://localhost:3000"},
    AllowMethods:     []string{"GET", "POST", "PUT", "DELETE"},
    AllowHeaders:     []string{"Content-Type", "Authorization"},
    AllowCredentials: true,
    MaxAge:           3600,
}))

Or use defaults:

app.Plug(middleware.CORS(middleware.DefaultCORS()))

JWT (Simplified)

One simple config - no more separate JWT and JWTChecks:

secret := []byte("your-secret-key")

app.Plug(middleware.JWT(middleware.JWTConfig{
    Secret:      secret,
    ContextKey:  "user",
    RequireExp:  true,
    Issuer:      "your-app",
    Audience:    "api",
    ClockSkew:   60 * time.Second,
    AllowedAlgs: []string{"HS256"},
}))

Or use defaults:

app.Plug(middleware.JWT(middleware.DefaultJWT(secret)))

Get user in handler:

app.GET("/me", func(c *zentrox.Context) {
    user, _ := c.Get("user")
    c.JSON(200, user)
})

Binding & Validation

type CreateUser struct {
    Name  string `json:"name" validate:"required,min=3,max=50"`
    Email string `json:"email" validate:"required,email"`
    Age   int    `json:"age" validate:"min=18,max=130"`
}

app.POST("/users", func(c *zentrox.Context) {
    var input CreateUser
    if err := c.BindJSONInto(&input); err != nil {
        c.Fail(400, "invalid input", err.Error())
        return
    }
    c.JSON(201, input)
})

Supported validators:

  • required - field must be present
  • min=N, max=N - min/max value or length
  • len=N - exact length
  • email - valid email
  • oneof=a b c - value must be one of
  • regex=pattern - match regex

Swagger/OpenAPI

Zentrox uses swaggo for API documentation with comment annotations:

1. Install swag CLI
go install github.com/swaggo/swag/cmd/swag@latest
2. Add annotations to your code
package main

import (
    "github.com/aminofox/zentrox"
    _ "yourapp/docs" // Import generated docs
)

// @title           My API
// @version         1.0
// @description     This is my API server
// @host            localhost:8000
// @BasePath        /api/v1

func main() {
    app := zentrox.NewApp()
    
    // Mount Swagger UI
    app.ServeSwagger("/swagger")
    
    // @Summary      Get user by ID
    // @Tags         users
    // @Param        id   path      int  true  "User ID"
    // @Success      200  {object}  User
    // @Router       /users/{id} [get]
    app.GET("/api/v1/users/:id", getUser)
    
    app.Run(":8000")
}
3. Generate documentation
swag init
4. Access Swagger UI

Visit http://localhost:8000/swagger/index.html

For more examples, see examples/swagger_annotations/ directory.


Context API

// Input
c.Param("id")           // Path parameter
c.Query("q")            // Query parameter
c.GetHeader("X-Token")  // Request header

// Binding
c.BindJSONInto(&dst)    // Bind & validate JSON
c.BindFormInto(&dst)    // Bind & validate form
c.BindQueryInto(&dst)   // Bind & validate query

// Output
c.JSON(200, data)       // Send JSON
c.String(200, "ok")     // Send text (with format support)
c.HTML(200, html)       // Send HTML
c.XML(200, data)        // Send XML
c.Data(200, "text/plain", bytes)  // Send raw bytes
c.SendStatus(200)       // Send status only
c.SetHeader("X-ID", id) // Response header

// Storage
c.Set("key", value)     // Store value
c.Get("key")            // Retrieve value

Performance

Zentrox is designed for speed:

  • Context pooling (zero allocations per request)
  • Fast routing (compiled trie)
  • Efficient middleware chain

Benchmarks on Apple M1 Pro:

  • ~1M rps for static routes
  • ~900K rps for parameterized routes
  • ~740K rps for JSON responses

Complete Example

package main

import (
    "github.com/aminofox/zentrox"
    "github.com/aminofox/zentrox/middleware"
    "time"
)

type User struct {
    ID    string `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email" validate:"required,email"`
}

func main() {
    app := zentrox.NewApp()

    // Global middleware
    app.Plug(
        middleware.CORS(middleware.DefaultCORS()),
        middleware.Recovery(),
        middleware.Logger(),
    )

    // Public routes
    app.GET("/", func(c *zentrox.Context) {
        c.String(200, "Welcome to Zentrox!")
    })

    app.GET("/ping", func(c *zentrox.Context) {
        c.JSON(200, map[string]string{"status": "ok"})
    })

    // API routes
    api := app.Scope("/api")
    
    api.GET("/users/:id", func(c *zentrox.Context) {
        user := User{
            ID:    c.Param("id"),
            Name:  "John Doe",
            Email: "[email protected]",
        }
        c.JSON(200, user)
    })

    api.POST("/users", func(c *zentrox.Context) {
        var user User
        if err := c.BindJSONInto(&user); err != nil {
            c.Fail(400, "invalid input", err.Error())
            return
        }
        user.ID = "generated-id"
        c.JSON(201, user)
    })

    // Protected routes
    secret := []byte("your-secret-key")
    admin := app.Scope("/admin", middleware.JWT(middleware.JWTConfig{
        Secret:     secret,
        RequireExp: true,
    }))

    admin.GET("/stats", func(c *zentrox.Context) {
        c.JSON(200, map[string]int{
            "users":  100,
            "orders": 50,
        })
    })

    app.Run(":8000")
}

Why Zentrox?

  • Minimal by Design: Only 6 essential middleware - no bloat, easy to understand
  • Clean API: Less boilerplate, cleaner patterns, better defaults
  • Easy Integration: Custom logger and JWT support to fit your existing systems
  • Faster routing: Compiled trie-based router with ~2M rps
  • Better defaults: Security and performance out of the box
  • Modern features: Built-in Swagger (via swaggo), validation, automatic middleware chaining
  • Production-ready: Context pooling, panic recovery, zero allocations

License

MIT

Documentation

Index

Constants

View Source
const (
	AppVersion  = "app_version"
	RequestID   = "request_id"
	XRequestID  = "X-Request-ID"
	TraceParent = "traceparent"
	TraceID     = "trace_id"
	SpanID      = "span_id"
)

Variables

This section is empty.

Functions

func NewHTTPError

func NewHTTPError(code int, message string, detail ...any) error

NewHTTPError constructs a new HTTPError as a Go error.

Types

type App

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

App is the main entrypoint of the framework.

func NewApp

func NewApp() *App

func (*App) DELETE

func (a *App) DELETE(path string, handlers ...Handler)

DELETE registers a route for DELETE requests

func (*App) GET

func (a *App) GET(path string, handlers ...Handler)

GET registers a route for GET requests

func (*App) Health

func (a *App) Health(livenessPath, readinessPath string, ready func() bool)

Health mounts tiny health endpoints onto the current App. - If livenessPath is non-empty, it returns 200 when the process is alive. - If readinessPath is non-empty and ready != nil, it returns 200/503 based on ready().

func (*App) ListRoutes

func (a *App) ListRoutes() []RouteInfo

Get route list (copy & sort for stability)

func (*App) PATCH

func (a *App) PATCH(path string, handlers ...Handler)

PATCH registers a route for PATCH requests

func (*App) POST

func (a *App) POST(path string, handlers ...Handler)

POST registers a route for POST requests

func (*App) PUT

func (a *App) PUT(path string, handlers ...Handler)

PUT registers a route for PUT requests

func (*App) Plug

func (a *App) Plug(m ...Handler)

Plug registers global middlewares in declared order.

func (*App) PrintRoutes

func (a *App) PrintRoutes(w io.Writer)

func (*App) Run

func (a *App) Run(addr string) error

Run keeps backward compatibility: starts a blocking server with production-leaning defaults. Equivalent to ListenAndServe.

func (*App) Scope

func (a *App) Scope(prefix string, mws ...Handler) *Scope

Scope creates a route group with a path prefix and optional middlewares.

func (*App) ServeHTTP

func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP uses a context pool and the precompiled router to handle the request.

func (*App) ServeSwagger

func (a *App) ServeSwagger(path string, options ...func(*httpSwagger.Config)) *App

ServeSwagger mounts Swagger UI at the specified path This is a convenience method that automatically sets up the route

Usage:

app.ServeSwagger("/swagger")
// Now accessible at: http://localhost:8000/swagger/index.html

func (*App) SetNotFound

func (a *App) SetNotFound(h Handler) *App

SetNotFound sets a custom 404 handler hook.

func (*App) SetOnPanic

func (a *App) SetOnPanic(fn func(*Context, any)) *App

SetOnPanic registers a hook called when a panic occurs. The panic value is forwarded and will be re-panicked after the hook returns.

func (*App) SetOnRequest

func (a *App) SetOnRequest(fn func(*Context)) *App

SetOnRequest registers a hook called at the start of handling a request.

func (*App) SetOnResponse

func (a *App) SetOnResponse(fn func(*Context, int, time.Duration)) *App

SetOnResponse registers a hook called after the request is handled. Parameters: (ctx, statusCode, latency).

func (*App) SetPrintRoutes

func (a *App) SetPrintRoutes(v bool) *App

Enable/disable route printing when server starts

func (*App) SetVersion

func (a *App) SetVersion(v string) *App

SetVersion configures an application version string injected per request.

func (*App) Shutdown

func (a *App) Shutdown(ctx context.Context, srv *http.Server) error

Shutdown requests a graceful stop. The server stops accepting new connections and waits for in-flight requests until ctx is done.

func (*App) Start

func (a *App) Start(cfg *ServerConfig) (*http.Server, error)

Start starts the server in a new goroutine and returns *http.Server. This is recommended in production to manage lifecycle explicitly.

func (*App) StartTLS

func (a *App) StartTLS(cfg *ServerConfig, certFile, keyFile string) (*http.Server, error)

StartTLS starts a TLS server in a new goroutine and returns *http.Server.

func (*App) Static

func (a *App) Static(prefix string, opt StaticOptions)

Static mounts a read-only file server under a prefix. It sets ETag and Last-Modified, and handles If-None-Match / If-Modified-Since. Security notes: - Prevents path traversal ("..") by cleaning and validating joined path. - Optional extension allow-list (if non-empty).

func (*App) Version

func (a *App) Version() string

Version returns the configured application version.

type Context

type Context struct {
	Writer  http.ResponseWriter
	Request *http.Request
	// contains filtered or unexported fields
}

Context carries request-scoped values and the middleware/handler chain.

func (*Context) Abort

func (c *Context) Abort()

Abort stops the middleware chain

func (*Context) Aborted

func (c *Context) Aborted() bool

Aborted returns true if the chain was aborted

func (*Context) Accepts

func (c *Context) Accepts(candidates ...string) string

Accepts returns the preferred type among provided candidates according to the request's "Accept" header. It returns the first element of candidates if the header is empty or no match is found.

func (*Context) BindFormInto

func (c *Context) BindFormInto(dst any) error

BindFormInto binds form data into dst and validates tags.

func (*Context) BindHeaderInto

func (c *Context) BindHeaderInto(dst any) error

BindHeaderInto maps request headers into a struct. Tag: `header:"X-Trace-Id,required"` ; if no tag -> use Canonical(FieldName).

func (*Context) BindInto

func (c *Context) BindInto(dst any) error

Binding & Validation BindInto auto-detects the binder (JSON/Form/Query), binds into dst, then validates tags.

func (*Context) BindJSONInto

func (c *Context) BindJSONInto(dst any) error

BindJSONInto binds JSON into dst and validates tags.

func (*Context) BindPathInto

func (c *Context) BindPathInto(dst any) error

BindPathInto maps path params (zentrox params) into a struct. Tag: `path:"id,required"` ; if no tag -> use lowerCamel(FldName).

func (*Context) BindQueryInto

func (c *Context) BindQueryInto(dst any) error

BindQueryInto binds query params into dst and validates tags.

func (*Context) ClearError

func (c *Context) ClearError()

ClearError clears the last recorded error.

func (*Context) Data

func (c *Context) Data(code int, contentType string, b []byte)

Data sends raw bytes with custom content type

func (*Context) Deadline

func (c *Context) Deadline() (time.Time, bool)

Deadline returns the time when work done on behalf of this request should be canceled. It proxies http.Request.Context().

func (*Context) Done

func (c *Context) Done() <-chan struct{}

Done returns a channel that is closed when the request context is canceled. It proxies http.Request.Context().

func (*Context) Download

func (c *Context) Download(filepath string, filename string)

func (*Context) Err

func (c *Context) Err() error

Err reports why the request context was canceled, if it was. It proxies http.Request.Context().

func (*Context) Error

func (c *Context) Error() error

Error returns the last recorded error, if any.

func (*Context) Fail

func (c *Context) Fail(code int, message string, detail ...any)

Fail sends a standardized HTTPError JSON and stops the chain.

func (*Context) Get

func (c *Context) Get(key string) (any, bool)

Get retrieves a value previously stored with Set.

func (*Context) GetHeader

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

func (*Context) HTML

func (c *Context) HTML(code int, html string)

HTML sends an HTML response

func (*Context) JSON

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

JSON sends a JSON response

func (*Context) Negotiate

func (c *Context) Negotiate(code int, candidates map[string]any)

Negotiate writes the response based on the request's Accept header. candidates is a map of content-type -> payload. Supported types out-of-the-box:

  • "application/json": payload marshaled as JSON (via SendJSON)
  • "text/plain": payload must be string
  • "text/html": payload must be string (HTML)
  • "application/xml": payload marshaled as XML (via SendXML)

Example:

c.Negotiate(200, map[string]any{
  "application/json": obj,
  "text/plain":       "hello",
})

func (*Context) Next

func (c *Context) Next()

Next executes the next handler in the middleware chain

func (*Context) Param

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

Param returns a path parameter value.

func (*Context) Problem

func (c *Context) Problem(status int, typeURI, title, detail, instance string, ext map[string]any)

Problem writes an application/problem+json response using the provided fields. The Content-Type is set to "application/problem+json".

func (*Context) Problemf

func (c *Context) Problemf(status int, title string, detail string)

Problemf is a convenience helper to write a simple problem without instance/ext.

func (*Context) PushSSE

func (c *Context) PushSSE(fn func(event func(name, data string)))

func (*Context) PushStream

func (c *Context) PushStream(fn func(w io.Writer, flush func()))

func (*Context) Query

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

Query returns a query parameter value.

func (*Context) RealIP

func (c *Context) RealIP() string

RealIP returns the client IP considering common reverse proxy headers. Order: X-Forwarded-For (first), X-Real-IP, then RemoteAddr fallback.

func (*Context) RequestID

func (c *Context) RequestID() string

RequestID returns the request ID if a RequestID middleware has stored it.

func (*Context) SaveUploadedFile

func (c *Context) SaveUploadedFile(field, dstDir string, opt UploadOptions) (string, error)

SaveUploadedFile reads file from multipart form by field name and writes it into dstDir. It validates extension (if provided), prevents path traversal, and can sanitize/generate names. Returns the full path saved to.

func (*Context) SendAttachment

func (c *Context) SendAttachment(path, filename string)

func (*Context) SendBytes

func (c *Context) SendBytes(code int, b []byte)

func (*Context) SendStatus

func (c *Context) SendStatus(code int)

func (*Context) Set

func (c *Context) Set(key string, v any)

Set stores an arbitrary value for the lifetime of the request.

func (*Context) SetError

func (c *Context) SetError(err error)

SetError records an error for the request (ErrorHandler can render it later).

func (*Context) SetHeader

func (c *Context) SetHeader(k, v string)

SetHeader sets a response header.

func (*Context) String

func (c *Context) String(code int, format string, values ...any)

String sends a plain text response

func (*Context) UploadedFile

func (c *Context) UploadedFile(field string, maxMemory int64) (multipart.File, *multipart.FileHeader, error)

UploadedFile returns the multipart file and header for advanced use. Caller must close the returned multipart.File.

func (*Context) Value

func (c *Context) Value(key any) any

Value implements context.Context and returns the value associated with this context for key,required Go 1.18+. Proxies http.Request.Context().Value(key).

func (*Context) XML

func (c *Context) XML(code int, v any)

XML sends an XML response

type HTTPError

type HTTPError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Detail  any    `json:"detail,omitempty"`
}

HTTPError is the canonical error payload returned by the framework.

func (HTTPError) Error

func (e HTTPError) Error() string

type Handler

type Handler func(*Context)

Handler is the middleware/handler function type.

func SwaggerHandler

func SwaggerHandler(options ...func(*httpSwagger.Config)) Handler

SwaggerHandler returns a handler that serves Swagger UI

Usage:

app.GET("/swagger/*any", zentrox.SwaggerHandler())

Or with custom config:

app.GET("/swagger/*any", zentrox.SwaggerHandler(
    httpSwagger.URL("http://localhost:8000/swagger/doc.json"),
))

func SwaggerJSON

func SwaggerJSON() Handler

SwaggerJSON serves the raw swagger.json file Useful if you want to expose the OpenAPI spec separately

Usage:

app.GET("/swagger.json", zentrox.SwaggerJSON())

func SwaggerYAML

func SwaggerYAML() Handler

SwaggerYAML serves the raw swagger.yaml file

Usage:

app.GET("/swagger.yaml", zentrox.SwaggerYAML())

type Problem

type Problem struct {
	Type     string         `json:"type,omitempty"`     // A URI reference that identifies the problem type
	Title    string         `json:"title,omitempty"`    // A short, human-readable summary of the problem type
	Status   int            `json:"status,omitempty"`   // HTTP status code generated by the origin server
	Detail   string         `json:"detail,omitempty"`   // Human-readable explanation specific to this occurrence
	Instance string         `json:"instance,omitempty"` // A URI reference that identifies the specific occurrence
	Ext      map[string]any `json:"-"`                  // extension members
}

Problem is a serializable RFC 9457 error object. Extension members are included when marshaled by merging Ext into the base object.

func (Problem) MarshalJSON

func (p Problem) MarshalJSON() ([]byte, error)

MarshalJSON merges extension members into the base JSON.

type RouteInfo

type RouteInfo struct {
	Method      string
	Path        string
	HandlerName string
	Middlewares []string
	File        string
	Line        int
}

type Scope

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

Scope (Route Group)

func (*Scope) DELETE

func (s *Scope) DELETE(path string, handlers ...Handler)

DELETE registers a route for DELETE requests

func (*Scope) GET

func (s *Scope) GET(path string, handlers ...Handler)

GET registers a route for GET requests

func (*Scope) PATCH

func (s *Scope) PATCH(path string, handlers ...Handler)

PATCH registers a route for PATCH requests

func (*Scope) POST

func (s *Scope) POST(path string, handlers ...Handler)

POST registers a route for POST requests

func (*Scope) PUT

func (s *Scope) PUT(path string, handlers ...Handler)

PUT registers a route for PUT requests

func (*Scope) Scope

func (s *Scope) Scope(prefix string, mws ...Handler) *Scope

Scope creates a nested route group with a path prefix and optional middlewares.

func (*Scope) Use

func (s *Scope) Use(middlewares ...Handler)

Use adds middleware to this scope

type ServerConfig

type ServerConfig struct {
	// Address to listen on, e.g. ":8000".
	Addr string

	// Timeouts protect the server from slow or stuck clients.
	// Defaults: ReadHeader=5s, Read=15s, Write=30s, Idle=60s.
	ReadHeaderTimeout time.Duration
	ReadTimeout       time.Duration
	WriteTimeout      time.Duration
	IdleTimeout       time.Duration

	// Upper bound for request headers (default 1 MiB).
	MaxHeaderBytes int

	// Where to write internal http.Server logs.
	// Default: stderr with prefix "zentrox/http: ".
	ErrorLog *log.Logger

	// BaseContext sets the base context for all connections (optional).
	BaseContext func(net.Listener) context.Context
}

ServerConfig controls the underlying http.Server configuration. All fields are optional; sensible defaults are applied.

type StaticOptions

type StaticOptions struct {
	// Directory on disk to serve from (absolute or relative to process cwd).
	Dir string
	// Optional index filename to serve when requesting the prefix root (e.g. "index.html").
	Index string
	// If true, do not auto-serve index when the request equals the prefix.
	DisableIndex bool
	// If non-zero, sets "Cache-Control: public, max-age=<seconds>" (otherwise no-cache).
	MaxAge time.Duration
	// If true, use strong ETag (SHA1 of content). Otherwise weak ETag (size-modtime).
	UseStrongETag bool
	// Optional allow-list of file extensions (lowercase, with dot), e.g. []string{".css",".js",".png"}.
	AllowedExt []string
}

StaticOptions controls behavior of Static(...)

type UploadOptions

type UploadOptions struct {
	// Maximum memory used by ParseMultipartForm; files larger than this are stored in temporary files.
	MaxMemory int64 // default 10 << 20 (10 MiB)
	// Allowed file extensions (lowercase, with dot). Empty means allow all.
	AllowedExt []string
	// If true, sanitize the base filename (only [a-zA-Z0-9._-]) to avoid weird characters.
	Sanitize bool
	// If true, always generate a unique filename (timestamp + random suffix).
	GenerateUniqueName bool
	// If false and file exists, returns error. If true, overwrite existing file.
	Overwrite bool
}

UploadOptions controls how files are accepted and saved.

Directories

Path Synopsis
examples
auth command
basic command
binding command
enterprise command
graceful command
gzip command
jwt_custom command
middleware command
minimal command
rendering command
swagger_annotations/docs
Package docs Code generated by swaggo/swag.
Package docs Code generated by swaggo/swag.
use_middleware command

Jump to

Keyboard shortcuts

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