README

GoDoc

Golax is the official go implementation for the Lax framework.

Related docs:

About Lax

Lax wants to be the best "user experience" for developers making REST APIs.

The design principles for Lax are:

  • The lowest language overhead
  • Extremely fast to develop
  • Very easy to read and trace.

Getting started

my_api := golax.NewApi()

my_api.Root.
    Interceptor(golax.InterceptorError).
    Interceptor(myLogingInterceptor)

my_api.Root.Node("hello").
    Method("GET", func(c *golax.Context) {
        // At this point, Root interceptors has been already executed
        fmt.Fprintln(c.Response, "Hello world!")
    })

my_api.Serve()

Routing example

Routing is based on nodes.

There are three types: static, regex and parameter.

  • static: Only matches with the url part if it is exactly the same.
  • regex: Surrounded by ( and ), if the regex match.
  • parameter: Surrounded by { and }, always matches.

Performance

The performance compared with the most popular alternative is very similar (actually golax performs slightly better) however code readability and maintainability is far better with golax implementation.

Tests has been executed in a Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz.

Learn more about this https://github.com/fulldump/golax-performance.

How interceptor works

If I want to handle a GET /users/1234/stats request, all interceptors in nodes from <root> to .../stats are executed:

Normal flow

To abort the execution, call to c.Error(404, "Resource not found"):

Break flow

Handling parameters

my_api := golax.NewApi()

my_api.Root.
    Node("users").
    Node("{user_id}").
    Method("GET", func (c *golax.Context) {
        fmt.Fprintln(c.Response, "You are looking for user " + c.Parameter)
    })

my_api.Serve()

It is also possible get all parameters:

func (c *golax.Context) {
    fmt.Fprintln(c.Response, "All parameters:", c.Parameters)
}

Support for Google custom methods

According to Google's API design guidelines to map RPC services to REST HTTP, it describes custom methods as extra operations that can not be easyly mapped to HTTP verbs. More info about custom methods

For example, this URL has a custom method :activate:

https://my.service.com/v1/users/31231231231:activate

Golax support custom methods as operations:

my_api.Root.
    Node("v1").
    Node("users").
    Node("{user_id}").
    Operation("activate").
    Method("POST", func(c *golax.Context) {
        user_id := c.Parameters["{user_id}"]"
        fmt.Fprintln(c.Response, "Here is custom method ':activate' for user "+user_id)
    })

Sample use cases

TODO: put here some examples to cover cool things:

  • fluent implementation
  • node cycling
  • readability
  • node preference
  • sample logging interceptor
  • sample auth interceptor
  • sample api errors

Documentation

Index

Constants

This section is empty.

Variables

View Source
var InterceptorError = &Interceptor{
	Documentation: Doc{
		Name: "Error",
		Description: `
Print JSON error in this form:

´´´json
{
	"status_code": 404,
	"error_code": 21,
	"description_code": "User '231223' not found."
}
´´´
		`,
	},
	After: func(c *Context) {
		if nil != c.LastError {
			json.NewEncoder(c.Response).Encode(c.LastError)
		}
	},
}

    *

    * `InterceptorError`
    * Print a JSON with the last error if exists
    
    View Source
    var InterceptorLog = &Interceptor{
    	Documentation: Doc{
    		Name: "Log",
    		Description: `
    Log all HTTP requests to stdout in this form:
    
    ´´´
    2016/02/20 11:09:17 GET	/favicon.ico	404	59B
    2016/02/20 11:09:34 GET	/service/v1/	405	68B
    2016/02/20 11:09:46 GET	/service/v1/doc	405	68B
    ´´´
    		`,
    	},
    	After: func(c *Context) {
    		log.Printf(
    			"%s\t%s\t%d\t%dB",
    			c.Request.Method,
    			c.Request.URL.RequestURI(),
    			c.Response.StatusCode,
    			c.Response.Length,
    		)
    	},
    }

      *

      * `InterceptorLog`
      * Log request and response
      
      View Source
      var InterceptorNoCache = &Interceptor{
      	Documentation: Doc{
      		Name: "InterceptorNoCache",
      		Description: `
      			Avoid caching via http headers
      		`,
      	},
      	Before: func(c *Context) {
      		add := c.Response.Header().Add
      
      		add("Cache-Control", "no-store, no-cache, must-revalidate, post-check=0, pre-check=0")
      		add("Pragma", "no-cache")
      		add("Expires", "0")
      	},
      }

        *

        * `InterceptorNoCache`
        * Send headers to disable browser caching
        

        Functions

        func SplitTail

        func SplitTail(s, sep string) []string

        Types

        type Api

        type Api struct {
        	Root       *Node
        	Prefix     string
        	Handler405 Handler
        	Handler404 Handler
        	Handler500 Handler
        }

        func NewApi

        func NewApi() *Api

        func (*Api) Serve

        func (a *Api) Serve()

        func (*Api) ServeHTTP

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

          *

          * This code is ugly but... It works! This is a critical part for the
          * performance, so it has to be written with love
          

          type Context

          type Context struct {
          	Request      *http.Request
          	Response     *ExtendedWriter
          	Parameter    string
          	Parameters   map[string]string
          	LastError    *ContextError
          	Scope        map[string]interface{}
          	PathHandlers string
          	// contains filtered or unexported fields
          }

          func NewContext

          func NewContext() *Context

          func (*Context) Error

          func (c *Context) Error(s int, d string) *ContextError

          func (*Context) Get

          func (c *Context) Get(k string) (interface{}, bool)

          func (*Context) Set

          func (c *Context) Set(k string, v interface{})

          type ContextError

          type ContextError struct {
          	StatusCode  int    `json: "status_code"`
          	ErrorCode   int    `json: "error_code"`
          	Description string `json: "description_code"`
          }

          type Doc

          type Doc struct {
          	Name        string
          	Description string
          	Ommit       bool
          }

          type ExtendedWriter

          type ExtendedWriter struct {
          	StatusCode int
          
          	Length int
          	http.ResponseWriter
          	// contains filtered or unexported fields
          }

            *

            * `Extended Writer`
            * It wraps http.ResponseWriter to add: StatusCode & Length
            

            func NewExtendedWriter

            func NewExtendedWriter(w http.ResponseWriter) *ExtendedWriter

            func (*ExtendedWriter) Write

            func (this *ExtendedWriter) Write(p []byte) (int, error)

            func (*ExtendedWriter) WriteHeader

            func (this *ExtendedWriter) WriteHeader(statusCode int)

            type Handler

            type Handler func(c *Context)

            type Interceptor

            type Interceptor struct {
            	Before        Handler
            	After         Handler
            	Documentation Doc
            }

            type Node

            type Node struct {
            	Interceptors         []*Interceptor
            	InterceptorsDeep     []*Interceptor
            	Methods              map[string]Handler
            	Children             []*Node
            	Documentation        Doc
            	DocumentationMethods map[string]Doc
            	Operations           map[string]*Operation
            	// contains filtered or unexported fields
            }

            func NewNode

            func NewNode() *Node

            func (*Node) Doc

            func (n *Node) Doc(d Doc) *Node

            func (*Node) GetPath

            func (n *Node) GetPath() string

            func (*Node) Interceptor

            func (n *Node) Interceptor(m *Interceptor) *Node

            func (*Node) InterceptorDeep

            func (n *Node) InterceptorDeep(m *Interceptor) *Node

            func (*Node) Method

            func (n *Node) Method(m string, h Handler, d ...Doc) *Node

            func (*Node) Node

            func (n *Node) Node(p string) *Node

            func (*Node) Operation

            func (n *Node) Operation(p string) *Operation

            func (*Node) SetPath

            func (n *Node) SetPath(p string)

            type Operation

            type Operation struct {
            	Path         string // Operation name
            	Interceptors []*Interceptor
            	Methods      map[string]Handler
            }

              Operation is a terminal node

              func NewOperation

              func NewOperation() *Operation

              func (*Operation) Interceptor

              func (o *Operation) Interceptor(m *Interceptor) *Operation

              func (*Operation) Method

              func (o *Operation) Method(m string, h Handler) *Operation

              Directories

              Path Synopsis