Skip to main content

Pine LogoPine

Build Faster, Understand Deeper

Simple Go server framework inspired by Express

Pine is built on the same concepts of ease of use such as Fiber golang or Express JS.

If you are familiar with Express or Fiber, Pine will be a breeze to use.

package main

import (
	"log"

	"github.com/BryanMwangi/pine"
)

func main() {
	app := pine.New()
	app.Get("/hello", func(c *pine.Ctx) error {
		return c.SendString("Hello World!")
	})

	log.Fatal(app.Start(":3000", "", ""))
}

Get started with Pine

Get Started

Why Pine?

There are a tonne of frameworks out there and often than not, they do not differ from each other. Pine is no different. However, what Pine wants to offer is not just a tool or a framework, but a place to learn how frameworks are actually built.

Understand how frameworks work and as you advance as a developer learn how to implement some of the solutions yourself. You can check out deep dive examples in our advanced documentation.

package main

import (
	"log"
    "net/http"

	"github.com/BryanMwangi/pine"
)

// example for a struct
type MyParams struct {
	Name string
	Age  int
}

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

  // bind params
  app.Get("/hello/:name", func(c *pine.Ctx) error {
      params := new(MyParams)
      err := c.BindParam("name", &params.Name)
      if err != nil {
        return c.SendStatus(http.StatusBadRequest)
      }
      return c.SendString("Hello " + params.Name)
  })

	log.Fatal(app.Start(":3000", "", ""))
}

Under the hood


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")
)

func (c *Ctx) BindParam(key string, v interface{}) error {
	param := c.Params(key)
	if param == "" {
		return ErrValidation
	}
	return bind(param, v)
}

// Internal helper function to validate the bind
// requires the input in this case is the key of the param or query
// and the destination is the value of the param or query
func bind(input string, destination interface{}) error {
	// reflect the type and value of the destination
	typ := reflect.TypeOf(destination)
	val := reflect.ValueOf(destination)

	// Check if the destination is a pointer
	if typ.Kind() != reflect.Ptr {
		return ErrPtr
	}

	// Dereference pointer type to assign value
	val = reflect.Indirect(val)

	switch val.Kind() {
	case reflect.String:
		val.SetString(input)
	case reflect.Int, reflect.Int64:
		parsed, err := strconv.ParseInt(input, 10, 64)
		if err != nil {
			return ErrConvert
		}
		val.SetInt(parsed)
	case reflect.Float64, reflect.Float32:
		parsed, err := strconv.ParseFloat(input, 64)
		if err != nil {
			return ErrConvert
		}
		val.SetFloat(parsed)
	case reflect.Bool:
		parsed, err := strconv.ParseBool(input)
		if err != nil {
			return ErrConvert
		}
		val.SetBool(parsed)
	default:
		return ErrType
	}
	return nil
}

Extreme performance

Pine offers exceptional performance compared to other frameworks. Currently Pine aims to compete with Express JS and Fiber. Pine is able to keep up or sometimes beat Fiber in synthetic tests. Benchmarks were carried out using Oha

Easy to Use

Pine prides itself on simplicity. The core principles of Pine are simplicity and performance.

Check under the hood

With Pine you are able to inspect and provide improvements to what runs Pine. Check out the advcanced documentation directory to see how Pine works.

Powered by Go

Easily one of the most readable and performant languages available and Pine takes full advantage of Go's speed and simplicity.