pine

package module
v1.1.7 Latest Latest
Warning

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

Go to latest
Published: Jul 2, 2026 License: MIT Imports: 18 Imported by: 0

README

Pine

Simple Go server framework built on the same concepts of ease of use such as Express JS

You can simply jump to the documentation

Getting Started

To get started you will need to import the go module by typing

go

go get github.com/BryanMwangi/pine

Quick start

Getting started with pine is easy. Here's a basic example to create a simple web server that responds with "Hello, World!" on the hello path.

package main

import (
    "log"

    "github.com/BryanMwangi/pine"
)

func main() {
    // Initialize a new Pine app
    app := pine.New()

    // Define a route for the GET method on the path '/hello'
    app.Get("/hello", func(c *pine.Ctx) error {
        return c.SendString("Hello World!")
    })

    // Start the server on port 3000
    log.Fatal(app.Start(":3000"))
}

Benchmarks

Pine is optimized for speed and performance while maintaining simplicity and ease of use. Being built on top of the standard http library, pine is able to handle a large number of requests with minimal overhead.

For the benchmarks we used oha. Since we are building alternatives for Express and Fiber, the benchmarks will only be against these 2 frameworks with more coming soon.

In the benchmark we tested a simple web server that responds with "pong" on the path "/ping". The benchmark was run on a MacBook Pro with a 2.9 GHz Intel Core i7 processor and 16 GB of RAM. Each server was sent 1,000,000 requests with a connection pool of 100.

Fun fact, by the time the Express benchmarks were finished, we had run the Pine and Fiber frameworks 5 times. The results were as follows:

Framework Requests/sec Avg Latency Slowest
Express 1966 1.328 ms 50.08 ms
Pine 77229 1.328 ms 19.07125 ms
Fiber 73959 1.302 ms 50.50235 ms

https://github.com/user-attachments/assets/a3ef09b1-4f2f-48e9-ae74-04a4bd47a95b

The results show that Pine is the fastest of the three frameworks tested. It is also the most performant of the three, with an average latency of 1.328 ms and a slowest latency of 19.07125 ms.

Limitations

  • No support for sessions out of the box

Advantages

  • Built on top of the standard net/http library. You can easily integrate pine with other features of the standard library without having to rewrite your code.
  • Built on top of the standard context.Context. This allows for easy integration with other libraries such as database connections.
  • Supports middleware
  • Out of the box support for helmet, cors and websockets
  • Supports cron jobs that are self managed by each Cron instance
Cron job example
package main

import (
    "log"
    "time"

    "github.com/BryanMwangi/pine"
	"github.com/BryanMwangi/pine/cron"
)

func main() {
	// Initialize a new Pine app
	app := pine.New()

	task := cron.Job{
		Fn:   logHello,
		Time: 6 * time.Second,
	}
	task2 := cron.Job{
		Fn:   logError,
		Time: 1 * time.Second,
	}
	task3 := cron.Job{
		Fn:   logHello2,
		Time: 3 * time.Second,
	}

	newCron := cron.New(cron.Config{
		RestartOnError: true,
		RetryAttempts:  3,
	})

	// There is no limit to the number of jobs you can add to the queue
	// However the queue size will impact the performance so be mindful and demure
	//
	// Also note that each task is executed in its own goroutine and performance
	// is relatively determined by the number of physical cores on your machine
	newCron.AddJobs(task, task2, task3)
	newCron.Start()

	// Define a route for the GET method on the root path '/hello'
	app.Get("/hello", func(c *pine.Ctx) error {
		return c.SendString("Hello World!")
	})

	// Start the server on port 3000
	log.Fatal(app.Start(":3000", "", ""))
}

func logHello() error {
	fmt.Println("Hello World!")
	return nil
}

// errors are handled internally depending on the restart policy of the cron
// If no restart policy is set, the error will cause the job to be removed
// immediately from the queue
func logError() error {
	return fmt.Errorf("Error")
}

func logHello2() error {
	fmt.Println("Another Hello World!")
	return nil
}

Roadmap

We aim to bring Pine to the same level as other popular frameworks. Some of the features we plan to add in the future include:

  • Session support and pooling
  • More middlewares out of the box such as CSRF etc.
  • Improvements to routing
  • Improvements to networking to improve performance from a low level approach
  • Add HTML rendering support
  • Add support for HTTP/3

Contributing

Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are highly appreciated. This version of pine is still very beta and improvements are definetly on their way. If you find a feature missing on pine and would like to add to it, please feel free to open a PR and we can definetly work together on it.

License

Distributed under the MIT License.

Documentation

Index

Constants

View Source
const (
	MethodGet     = "GET"
	MethodPost    = "POST"
	MethodPut     = "PUT"
	MethodDelete  = "DELETE"
	MethodPatch   = "PATCH"
	MethodHead    = "HEAD"
	MethodOptions = "OPTIONS"
)
View Source
const (
	DefaultBodyLimit = 5 * 1024 * 1024
)

Variables

View Source
var (
	ErrParse      = errors.New("bind: cannot parse")
	ErrConvert    = errors.New("bind: cannot convert")
	ErrType       = errors.New("bind: unexpected type")
	ErrPtr        = errors.New("bind: destination must be a pointer")
	ErrValidation = errors.New("bind: validation failed")
)
View Source
var (
	ErrURIRequired    = errors.New("uri is required")
	ErrMethodRequired = errors.New("method is required")
	ErrResponseIsNil  = errors.New("response is nil")
)

Common errors if you want to use the client and its methods

DefaultMethods is the set of HTTP methods Pine accepts by default.

View Source
var (
	ErrFileName = errors.New("could not determine file name")
)

Functions

func StatusMessage

func StatusMessage(status int) string

StatusMessage returns the text for a given HTTP status code.

Types

type Client added in v1.0.5

type Client struct {
	*http.Client
	// contains filtered or unexported fields
}

Client is a wrapper around the http.Client Has methods for setting the request URI, method, headers, and body

Ideally you want to use a client only once, however if you need to use multiple instances of the same client, start by creating a new client and then call the SetRequestURI, SetMethod, SetHeaders, and SetBody methods

Note that if you call SendRequest, the old response will be overwritten If you would like to store the response, call ReadResponse and store the response in a variable of your own choosing

func NewClient added in v1.0.5

func NewClient() *Client

Call this to create a new client You can then call SetRequestURI, SetMethod, SetHeaders, and SetBody after creating the client

func NewClientWithTimeout added in v1.0.5

func NewClientWithTimeout(timeout time.Duration) *Client

NewClientWithTimeout returns a new client with a timeout

func (*Client) ReadResponse added in v1.0.5

func (c *Client) ReadResponse() (code int, body []byte, err error)

Call this method to get the response from the request Note that after calling this method the old reponse will be discarded

Attempts to read the response after calling this method more than once will return an error

func (*Client) Request added in v1.0.5

func (c *Client) Request() *Request

func (*Client) SendRequest added in v1.0.5

func (c *Client) SendRequest() error

Call this method only if you have already set the request URI and method

body and headers are optional

It returns an error if the request URI or method is not set Please note that if you call SendRequest, the old response will be overwritten

func (*Client) SetTLSVerification added in v1.0.5

func (c *Client) SetTLSVerification(skip bool)

Use this method to skip TLS verification This can be useful if the api you are calling has outdated TLS certificates

type Config

type Config struct {
	// Defines the body limit for a request.
	// -1 will decline any body size
	//
	// Default: 5 * 1024 * 1024
	BodyLimit int64

	// Default: 5 Seconds
	ReadTimeout time.Duration

	// Default: 5 Seconds
	WriteTimeout time.Duration

	// Default: false
	DisableKeepAlive bool

	// Default: json.Marshal
	JSONEncoder JSONMarshal

	// Default: json.Unmarshal
	JSONDecoder JSONUnmarshal

	// RequestMethods provides customizability for HTTP methods.
	//
	// Default: DefaultMethods
	RequestMethods []string

	// UploadPath is the path where uploaded files will be saved.
	//
	// Default: ./uploads
	UploadPath string

	// StaticPath is the path where static files will be served.
	//
	// Default: "static"
	StaticPath string

	// ViewPath is the path where view files will be served.
	//
	// Default: "views"
	ViewPath string

	// Engine is the template engine to use.
	//
	// Default: html
	Engine string

	// ReloadTemplates re-parses template files from disk on every render call.
	// Useful during development so edits are visible without a server restart.
	// Leave false in production — templates are parsed once at startup.
	//
	// Default: false
	ReloadTemplates bool

	// TLSConfig is the configuration for TLS.
	TLSConfig TLSConfig
}

Config is a struct holding the server settings.

type Cookie struct {
	Name       string
	Value      string
	Path       string
	Domain     string
	Expires    time.Time
	RawExpires string
	MaxAge     int
	Secure     bool
	HttpOnly   bool
	SameSite   SameSite
	Raw        bool
	Unparsed   []string
}

Cookie defines the structure of an HTTP cookie.

type Ctx

type Ctx struct {
	Server   *Server                // Reference to *Server
	Method   string                 // HTTP method
	BaseURI  string                 // HTTP base uri
	Request  *http.Request          // HTTP request
	Response *responseWriterWrapper // HTTP response writer
	// contains filtered or unexported fields
}

func (*Ctx) BindJSON added in v1.0.5

func (c *Ctx) BindJSON(v interface{}) error

BindJSON binds the request body to the given interface. You can use this to validate the request body without adding further logic to your handlers.

Tested with nested JSON objects and arrays If you notice any errors, please open an issue on Github and I will fix it right away

func (*Ctx) BindParam added in v1.0.5

func (c *Ctx) BindParam(key string, v interface{}) error

BindParam binds the specified parameter value of a request.

func (*Ctx) BindQuery added in v1.0.5

func (c *Ctx) BindQuery(key string, v interface{}) error

BindQuery binds the specified query value of a request.

func (*Ctx) Context

func (c *Ctx) Context() context.Context

Context returns the underlying request context.

func (*Ctx) DeleteCookie

func (c *Ctx) DeleteCookie(names ...string) *Ctx

DeleteCookie expires the named cookies by setting Max-Age=-1.

func (*Ctx) FormFile added in v1.0.7

func (c *Ctx) FormFile(key string) (multipart.File, *multipart.FileHeader, error)

func (*Ctx) Header

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

Header returns the request header value for the given key.

func (*Ctx) IP

func (c *Ctx) IP() string

IP returns the client IP address, checking proxy headers first.

func (*Ctx) IPs added in v1.1.3

func (c *Ctx) IPs() []string

IPs returns all IP addresses from proxy headers.

func (*Ctx) JSON

func (c *Ctx) JSON(data interface{}, status ...int) error

JSON encodes data as JSON and writes it to the response.

func (*Ctx) Locals

func (c *Ctx) Locals(key string, value ...interface{}) interface{}

Locals gets or sets a request-scoped value.

func (*Ctx) MultipartForm added in v1.0.7

func (c *Ctx) MultipartForm() *multipart.Form

func (*Ctx) MultipartFormValue added in v1.0.7

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

func (*Ctx) MultipartReader added in v1.0.7

func (c *Ctx) MultipartReader(key string) (*multipart.Reader, error)

func (*Ctx) Next

func (c *Ctx) Next() error

Next is intended for use inside handler chains registered via AddRoute. It advances the handler index but the actual dispatch loop in ServeHTTP already calls all handlers sequentially; Next() is here for API parity.

func (*Ctx) Params

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

Params returns the URL parameter for key.

func (*Ctx) ParamsInt

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

ParamsInt returns the URL parameter converted to int.

func (*Ctx) Query

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

Query returns the URL query parameter for key.

func (*Ctx) ReadCookie

func (c *Ctx) ReadCookie(name string) (*Cookie, error)

ReadCookie reads a named cookie from the request.

func (*Ctx) RemoteAddr added in v1.1.3

func (c *Ctx) RemoteAddr() string

RemoteAddr returns the raw remote address of the client.

func (*Ctx) Render added in v1.1.4

func (c *Ctx) Render(name string, data interface{}, status ...int) error

Render executes a named template and writes the result to the response.

name is the template filename relative to ViewPath, e.g. "index.html" or "admin/dashboard.html". data is the dot value passed to the template. status defaults to 200 when omitted.

The output is buffered so a template error never sends a partial response. When render.LiveReload() is active the engine automatically appends the live-reload <script> — no manual template changes needed.

func (*Ctx) SaveFile added in v1.0.7

func (c *Ctx) SaveFile(fh *multipart.FileHeader, path ...string) error

SaveFile saves the file to the specified path or the default upload path if no path is specified

func (*Ctx) SendFile added in v1.0.7

func (c *Ctx) SendFile(filePath string) error

func (*Ctx) SendStatus

func (c *Ctx) SendStatus(status int) error

SendStatus writes the status code and its text body.

func (*Ctx) SendString

func (c *Ctx) SendString(body string) error

SendString writes a plain-text string to the response.

func (*Ctx) Set

func (c *Ctx) Set(key string, val interface{}) *Ctx

Set sets a response header.

func (*Ctx) SetCookie

func (c *Ctx) SetCookie(cookies ...Cookie) *Ctx

SetCookie adds one Set-Cookie header per cookie (RFC 6265 §4.1).

func (*Ctx) Status

func (c *Ctx) Status(status int) *Ctx

Status sets the HTTP response status code.

func (*Ctx) StreamFile added in v1.0.7

func (c *Ctx) StreamFile(filePath string) error

type Group added in v1.1.6

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

Group is a route group with a shared URL prefix and optional scoped middleware. Routes registered on a Group inherit the prefix and middleware of all parent groups. Create one via Server.Group or nest with Group.Group.

func (*Group) AddRoute added in v1.1.6

func (g *Group) AddRoute(method, path string, handlers ...Handler)

AddRoute registers a route under the group's prefix with group middleware applied.

func (*Group) Delete added in v1.1.6

func (g *Group) Delete(path string, handlers ...Handler)

func (*Group) Get added in v1.1.6

func (g *Group) Get(path string, handlers ...Handler)

func (*Group) Group added in v1.1.6

func (g *Group) Group(prefix string, middlewares ...Middleware) *Group

Group creates a sub-group rooted at prefix relative to this group's prefix. Middleware passed here is appended after the parent group's middleware, so execution order is: global → parent-group → sub-group → handler.

func (*Group) Options added in v1.1.6

func (g *Group) Options(path string, handlers ...Handler)

func (*Group) Patch added in v1.1.6

func (g *Group) Patch(path string, handlers ...Handler)

func (*Group) Post added in v1.1.6

func (g *Group) Post(path string, handlers ...Handler)

func (*Group) Put added in v1.1.6

func (g *Group) Put(path string, handlers ...Handler)

type Handler

type Handler = func(*Ctx) error

type JSONMarshal

type JSONMarshal func(v interface{}) ([]byte, error)

type JSONUnmarshal

type JSONUnmarshal func(data []byte, v interface{}) error

type Middleware

type Middleware func(Handler) Handler

type Reloader added in v1.1.4

type Reloader interface {
	Rebuild() error
}

Reloader is an optional extension of ViewEngine. Engines that support hot-reloading implement this so RebuildViews() can re-parse templates from disk without restarting the server.

type Request added in v1.0.5

type Request struct {
	http.Request
	// contains filtered or unexported fields
}

func (*Request) JSON added in v1.0.5

func (r *Request) JSON(body interface{}) error

Sets the body of the request as JSON

func (*Request) SetHeaders added in v1.0.5

func (r *Request) SetHeaders(headers map[string]string)

Use this to set the headers of the request You can add as many headers as you want in a map

For example:

headers := map[string]string{
	"X-API-KEY": "1234567890",
}

request.SetHeaders(headers)

func (*Request) SetMethod added in v1.0.5

func (r *Request) SetMethod(method string) *Request

Use this to set the method of the request

For example: request.SetMethod("GET")

func (*Request) SetRequestURI added in v1.0.5

func (r *Request) SetRequestURI(uri string) *Request

Use this to set the url of the request

For example: request.SetRequestURI("https://example.com/api/v1/users")

type Route

type Route struct {
	Method   string    `json:"method"`
	Path     string    `json:"path"`
	Handlers []Handler `json:"-"`
}

Route holds all metadata for each registered handler.

type Router added in v1.1.4

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

Router is a radix-tree HTTP router. Insert registers a route; Search looks one up.

func (*Router) Insert added in v1.1.4

func (r *Router) Insert(method, path string, handlers []Handler)

Insert registers handlers for a given HTTP method and path. Segments starting with ':' are treated as named parameters. A trailing '/*' is treated as a catch-all wildcard.

func (*Router) Search added in v1.1.4

func (r *Router) Search(method, path string) ([]Handler, map[string]string, bool)

Search finds the handlers registered for method+path. Returns (handlers, params, found).

func (*Router) SearchAnyMethod added in v1.1.4

func (r *Router) SearchAnyMethod(path string) ([]Handler, map[string]string, bool)

SearchAnyMethod returns handlers for path regardless of method. Used for OPTIONS / CORS preflight fallback.

type SameSite

type SameSite int

SameSite controls the SameSite cookie attribute. Use SameSiteUnset (0) to omit the attribute.

const (
	SameSiteUnset  SameSite = iota // 0 — omit SameSite directive
	SameSiteLax                    // 1
	SameSiteStrict                 // 2
	SameSiteNone                   // 3
)

type Server

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

func New

func New(config ...Config) *Server

New creates and returns a new Pine server.

func (*Server) AddRoute

func (server *Server) AddRoute(method, path string, handlers ...Handler)

AddRoute registers a route for the given method and path.

func (*Server) AddShutdownHook added in v1.1.4

func (server *Server) AddShutdownHook(hooks ...func())

AddShutdownHook registers fn to be called during graceful shutdown. This lets packages like render register cleanup callbacks (e.g. stopping the file watcher) without requiring the caller to wire them up manually.

func (*Server) Delete

func (server *Server) Delete(path string, handlers ...Handler)

func (*Server) Get

func (server *Server) Get(path string, handlers ...Handler)

func (*Server) Group added in v1.1.6

func (server *Server) Group(prefix string, middlewares ...Middleware) *Group

Group returns a route group rooted at prefix. Middleware passed here applies to every route registered on the group or its sub-groups, but not to routes registered directly on the server.

func (*Server) Options added in v1.0.4

func (server *Server) Options(path string, handlers ...Handler)

func (*Server) Patch

func (server *Server) Patch(path string, handlers ...Handler)

func (*Server) Post

func (server *Server) Post(path string, handlers ...Handler)

func (*Server) Put

func (server *Server) Put(path string, handlers ...Handler)

func (*Server) RebuildViews added in v1.1.4

func (server *Server) RebuildViews() error

RebuildViews re-parses all template files from disk by delegating to the engine's Rebuild() method. Called by render.LiveReload() on every detected template file change. No-op when the engine does not implement Reloader.

func (*Server) ReloadTemplates added in v1.1.4

func (server *Server) ReloadTemplates() bool

ReloadTemplates reports whether the server was configured with hot-reloading enabled. render.Setup() reads this to decide whether to start LiveReload.

func (*Server) ServeHTTP

func (server *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)

func (*Server) ServeShutDown

func (server *Server) ServeShutDown(ctx context.Context, hooks ...func()) error

ServeShutDown gracefully shuts the server down.

func (*Server) SetEngine added in v1.1.4

func (server *Server) SetEngine(engine ViewEngine)

SetEngine installs a template engine on the server. Call this (or render.Setup()) before registering routes that use Ctx.Render().

func (*Server) Start

func (server *Server) Start(address string) error

Start listens on address and serves requests.

func (*Server) Use

func (server *Server) Use(middleware Middleware)

Use appends a middleware and rebuilds the router so every existing route is re-wrapped from its original handlers — preventing double-wrapping.

func (*Server) ViewPath added in v1.1.4

func (server *Server) ViewPath() string

ViewPath returns the directory configured for view templates.

type TLSConfig added in v1.0.5

type TLSConfig struct {
	ServeTLS bool
	CertFile string
	KeyFile  string
}

type ViewEngine added in v1.1.4

type ViewEngine interface {
	Render(w io.Writer, name string, data interface{}) error
}

ViewEngine is the interface every template backend must implement. Register an engine with Server.SetEngine(); the render package provides the built-in HTML engine via render.Setup().

Directories

Path Synopsis
Examples
BindExample command
CacheExample command
CronExample command
FileExample command
FullStackApp command
FullStackApp demonstrates Pine's HTML renderer alongside JSON API routes and static file serving.
FullStackApp demonstrates Pine's HTML renderer alongside JSON API routes and static file serving.
GroupExample command
SimpleRender command
SimpleServer command
Websocket command
Package render provides Pine's built-in HTML template engine.
Package render provides Pine's built-in HTML template engine.
Pine's websocket package is a websocket server that supports multiple channels This feature is experimental and may change in the future.
Pine's websocket package is a websocket server that supports multiple channels This feature is experimental and may change in the future.

Jump to

Keyboard shortcuts

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