ply

package module
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Nov 6, 2026 License: MIT Imports: 23 Imported by: 0

README

Ply

One ply over fasthttp. A thin, zero-allocation HTTP layer for Go.

Go Benchmarks Go Report Card Coverage Contributions License Last Commit Issues

Ply uses Tiered Zero-Copy Routing (TZCR) to split routes into three levels: static, parametric, and wildcard. Routes are precompiled and kept in a cache-aware structure for fast, allocation-free matching. Middleware runs through chained handlers, and route groups support custom prefixes and middleware. This keeps Ply fast without giving up flexibility.

Key features

  • Middleware-first via .Use()
  • Fast router and context engine
  • Built-in async engines (PubSub, Queue, Cache)
  • Extensible and clean internal architecture
  • Optional middleware: Auth, Logger, Recovery
  • Test helpers: app.Test(req) runs requests in-process, no port needed
  • CLI generator: npx create-ply for instant project scaffolding

Authentication

Ply ships a JWT middleware for protecting routes. Within your app setup, after app := ply.New():

app.Use(middleware.AuthJWT(os.Getenv("JWT_SECRET")))

Import os and github.com/Dziqha/ply/middleware.

Protected requests must send an Authorization: Bearer <token> header. Missing or invalid tokens are rejected with 401. See the Auth middleware docs for details.

Set the secret before starting the server:

export JWT_SECRET=supersecurekey123

Getting started

Install
go get github.com/Dziqha/ply
Scaffold a project
npx create-ply@latest myapp

The generator prompts for a feature name and produces a feature-based project layout that is ready to extend. Node.js is required only for the scaffolding CLI. Ply itself has no Node.js dependency.

Minimal application
package main

import (
	"github.com/Dziqha/ply"
	"github.com/Dziqha/ply/core"
)

func main() {
	app := ply.New()

	app.Get("/", func(c *core.Context) {
		c.Text(200, "Hello from Ply")
	})

	app.RunServer(":8080")
}

Run it with:

go run main.go

The server listens on http://localhost:8080.


Benchmarks

Measured on a 6C/12T Ryzen 5 5600H with Go 1.26, wrk on the same host, round-robin reps, median of 3. Method, spread and caveats are documented in Benchmarks.

Workload Ply fasthttp Fiber v3 Gin Echo
GET /user/42, c=200, t=6 442,483 453,821 439,446 264,289 256,752
GET /user/42, c=512, t=12 450,054 462,022 441,530 275,065 270,836
In-process, per request 180 ns 173 ns (bare path+switch) 305 ns n/a n/a

Ply is a thin layer over fasthttp: within 3% of the engine and 1-2% of Fiber in the thread-matched run, and inside the noise band at oversubscribed concurrency. In-process it costs about 7 ns more than a bare path-plus-switch handler. The value Ply adds is zero-allocation routing and built-in engines, not a different HTTP engine.


Contributing

Bug reports, feature requests and pull requests are welcome. Use Issues for bugs and Discussions for questions and design proposals.

License

This project is licensed under the MIT License.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Banner(addr string) string

func CenterText

func CenterText(text string) string

Types

type App

type App struct {
	EngineCtx *core.EngineContext

	ErrorHandler func(*core.Context, error)
	// contains filtered or unexported fields
}

func New

func New() *App

func (*App) Add

func (a *App) Add(methods []string, path string, h core.Handler, hs ...core.Handler) *router.Route

func (*App) All

func (a *App) All(path string, handler core.Handler, handlers ...core.Handler) *router.Route

func (*App) Connect

func (a *App) Connect(path string, h core.Handler, hs ...core.Handler) *router.Route

func (*App) Delete

func (a *App) Delete(path string, handler core.Handler, handlers ...core.Handler) *router.Route

func (*App) Get

func (a *App) Get(path string, handler core.Handler, handlers ...core.Handler) *router.Route

HTTP method shortcuts

func (*App) GetFast

func (a *App) GetFast(path string, handler fasthttp.RequestHandler) *router.Route

func (*App) Group

func (a *App) Group(prefix string, middlewares ...core.Handler) *Group

func (*App) Handler

func (a *App) Handler() fasthttp.RequestHandler

func (*App) Head

func (a *App) Head(path string, handler core.Handler, handlers ...core.Handler) *router.Route

func (*App) MethodNotAllowed

func (a *App) MethodNotAllowed(h core.Handler) *App

func (*App) NotFound

func (a *App) NotFound(h core.Handler) *App

func (*App) Options

func (a *App) Options(path string, handler core.Handler, handlers ...core.Handler) *router.Route

func (*App) Patch

func (a *App) Patch(path string, handler core.Handler, handlers ...core.Handler) *router.Route

func (*App) Post

func (a *App) Post(path string, handler core.Handler, handlers ...core.Handler) *router.Route

func (*App) Put

func (a *App) Put(path string, handler core.Handler, handlers ...core.Handler) *router.Route

func (*App) Route

func (a *App) Route(path string) *router.Route

func (*App) RunServer

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

func (*App) RunTLSServer

func (a *App) RunTLSServer(addr, certFile, keyFile string) error

func (*App) RunWithGracefulShutdown

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

func (*App) SetErrorHandler

func (a *App) SetErrorHandler(fn func(*core.Context, error)) *App

func (*App) Shutdown

func (a *App) Shutdown(ctx context.Context) error

func (*App) Static

func (a *App) Static(prefix, dir string) *App

func (*App) Templates

func (a *App) Templates(dir string) *App

func (*App) Test

func (a *App) Test(req *http.Request) *http.Response

Test runs req through the full application (router, middleware, recovery) without opening a network listener, and returns the recorded response. It is meant for tests:

req := httptest.NewRequest(http.MethodGet, "/user/42", nil)
resp := app.Test(req)
if resp.StatusCode != http.StatusOK { ... }
body, _ := io.ReadAll(resp.Body)

The response body is buffered, so it stays readable after the call. The handler is cached after the first call; routes registered later are still matched because the router rebuilds its table lazily.

func (*App) Use

func (a *App) Use(args ...any) *App

func (*App) WithCache

func (a *App) WithCache() *App

func (*App) WithPubsub

func (a *App) WithPubsub() *App

func (*App) WithQueue

func (a *App) WithQueue() *App

func (*App) WrapHandlers

func (a *App) WrapHandlers(hs []core.Handler) fasthttp.RequestHandler

type Group

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

func (*Group) Add

func (g *Group) Add(methods []string, path string, h core.Handler, hs ...core.Handler) *router.Route

func (*Group) All

func (g *Group) All(path string, h core.Handler, hs ...core.Handler) *router.Route

func (*Group) Delete

func (g *Group) Delete(path string, h core.Handler, hs ...core.Handler) *router.Route

func (*Group) Get

func (g *Group) Get(path string, h core.Handler, hs ...core.Handler) *router.Route

func (*Group) Options

func (g *Group) Options(path string, h core.Handler, hs ...core.Handler) *router.Route

func (*Group) Patch

func (g *Group) Patch(path string, h core.Handler, hs ...core.Handler) *router.Route

func (*Group) Post

func (g *Group) Post(path string, h core.Handler, hs ...core.Handler) *router.Route

func (*Group) Put

func (g *Group) Put(path string, h core.Handler, hs ...core.Handler) *router.Route

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

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