poteto

package module
v1.6.1 Latest Latest
Warning

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

Go to latest
Published: Mar 1, 2025 License: MIT Imports: 22 Imported by: 0

README

Poteto

Simple Web Framework of GoLang

We have confirmed that it works with various versions: go@1.21.x, go@1.22.x, go@1.23.x

go get -u github.com/poteto-go/poteto@latest
# or
go mod tidy

Example App For Poteto

https://github.com/poteto-go/poteto-examples

Poteto-Cli

We support cli tool. But if you doesn't like it, you can create poteto-app w/o cli of course.

You can start hot-reload poteto app.

go install github.com/poteto-go/poteto-cli/cmd/poteto-cli@latest

OR build from docker image

https://hub.docker.com/repository/docker/poteto17/poteto-go/general

docker pull poteto17/poteto-go
docker -it --rm poteto17/poteto-go:1.23 bash

detail on:

https://github.com/poteto-go/poteto-cli

Poteto Option

WITH_REQUEST_ID=true
DEBUG_MODE=false
LISTENER_NETWORK=tcp

Feature

Test Without Server (>=1.4.0)

[!NOTE] Poteto developers can easily test without setting up a server.

func main() {
	p := poteto.New()

	p.GET("/users", func(ctx poteto.Context) error {
		return ctx.JSON(http.StatusOK, map[string]string{
			"id":   "1",
			"name": "tester",
		})
	})

	res := p.Play(http.MethodGet, "/users")
	resBodyStr := res.Body.String
	// => {"id":"1","name":"tester"}
}
Leaf router & middlewareTree (>=0.21.0)
func main() {
	p := poteto.New()

	// Leaf >= 0.21.0
	p.Leaf("/users", func(userApi poteto.Leaf) {
		userApi.Register(middleware.CamaraWithConfig(middleware.DefaultCamaraConfig))
		userApi.GET("/", controller.UserHandler)
		userApi.GET("/:name", controller.UserIdHandler)
	})

	p.Run("127.0.0.1:8000")
}
Get RequestId Easily
func handler(ctx poteto.Context) error {
	requestId := ctx.RequestId()
}
JSONRPCAdapter (>=0.26.0)

KeyNote: You can serve JSONRPC server easily.

type (
  Calculator struct{}
  AdditionArgs   struct {
    Add, Added int
  }
)

func (tc *TestCalculator) Add(r *http.Request, args *AdditionArgs) int {
 return args.Add + args.Added
}

func main() {
  p := New()

  rpc := TestCalculator{}
  // you can access "/add/Calculator.Add"
  p.POST("/add", func(ctx Context) error {
    return PotetoJsonRPCAdapter[Calculator, AdditionArgs](ctx, &rpc)
  })

  p.Run("8080")
}

How to use

package main

import (
	"net/http"

	"github.com/poteto-go/poteto"
	"github.com/poteto-go/poteto/middleware"
)

func main() {
	p := poteto.New()

	// CORS
	p.Register(middleware.CORSWithConfig(
		middleware.CORSConfig{
			AllowOrigins: []string{"*"},
			AllowMethods: []string{http.MethodGet, http.MethodPut, http.MethodPost, http.MethodDelete},
		},
	))

	// Leaf >= 0.21.0
	p.Leaf("/users", func(userApi poteto.Leaf) {
		userApi.Register(middleware.CamaraWithConfig(middleware.DefaultCamaraConfig))
		userApi.GET("/", controller.UserHandler)
		userApi.GET("/:name", controller.UserNameHandler)
	})

	p.Run("127.0.0.1:8000")
}

type User struct {
	Name any `json:"name"`
}

func UserHandler(ctx poteto.Context) error {
	user := User{
		Name: "user",
	}
	return ctx.JSON(http.StatusOK, user)
}

func UserNameHandler(ctx poteto.Context) error {
	name, _ := ctx.PathParam("name")
	user := User{
		Name: name,
	}
	return ctx.JSON(http.StatusOK, user)
}

Documentation

Index

Constants

View Source

Written By https://lazesoftware.com/ja/tool/brailleaagen/

Variables

This section is empty.

Functions

func NewHttpError

func NewHttpError(code int, messages ...any) *httpError

func PotetoJsonRPCAdapter

func PotetoJsonRPCAdapter[T any, S any](ctx Context, api *T) error

inspired by https://github.com/kanocz/goginjsonrpc/blob/master/jsonrpc.go * Only Support "POST" method

Types

type Binder

type Binder interface {
	// Bind request body -> &object
	//
	// if unset "Content-Type: application/json", return perror.ErrUnsetHeaderApplicationJson
	//
	// if zero length content, return perror.ErrZeroLengthContent
	//
	Bind(ctx Context, object any) error

	// Bind with github.com/go-playground/validator/v10
	BindWithValidate(ctx Context, object any) error
}

func NewBinder

func NewBinder() Binder

type Context

type Context interface {
	// return status code & json response
	//
	// set Content-Type: application/json
	JSON(code int, value any) error

	JSONRPCError(code int, message string, data string, id int) error

	// decode body -> interface
	//
	// You need "Content-Type: application/json" in request Header
	//
	// func handler(ctx poteto.Context) error {
	//   user := User{}
	//   ctx.Bind(&user)
	// }
	Bind(object any) error

	// Bind with github.com/go-playground/validator/v10
	//
	// type User struct {
	//   Name string `json:"name"`
	//   Mail string `json:"mail" validate:"required,email"`
	// }
	//
	// if request body = {"name":"test", "mail":"example"}
	//
	// func handler(ctx poteto.Context) error {
	//   user := User{}
	//   err := ctx.BindWithValidate(&user) // caused error
	// }
	BindWithValidate(object any) error

	WriteHeader(code int)

	JsonSerialize(value any) error

	JsonDeserialize(object any) error

	SetQueryParam(queryParams url.Values)

	SetParam(paramType string, paramUnit ParamUnit)

	// Get path parameter
	// func handler(ctx poteto.Context) error {
	//   id, ok := ctx.PathParam("id")
	// }
	PathParam(key string) (string, bool)

	// Get path parameter
	// func handler(ctx poteto.Context) error {
	//   id, ok := ctx.QueryParam("id")
	// }
	QueryParam(key string) (string, bool)

	// DebugParam return all http parameters
	//
	// use for debug or log
	//
	// EX: {"path":{"player_id":"2"},"query":{"user_id":"1"}}
	DebugParam() (string, bool)

	SetPath(path string)
	GetPath() string

	// set (map[string]any) -> context
	// you can get from ctx.Get
	//
	// in your middleware
	// func middleware(next poteto.HandlerFunc) poteto.HandlerFunc {
	//   return func(ctx poteto.Context) error {
	//     ctx.Set("foo", "bar")
	//   }
	// }
	//
	// in your handler
	// func handler(ctx poteto.Context) error {
	//   val, ok := ctx.Get("foo")
	// }
	Set(key string, val any)

	// get (any, ok) <- context
	// you can set value by ctx.Set
	//
	// in your middleware
	// func middleware(next poteto.HandlerFunc) poteto.HandlerFunc {
	//   return func(ctx poteto.Context) error {
	//     ctx.Set("foo", "bar")
	//   }
	// }
	//
	// in your handler
	// func handler(ctx poteto.Context) error {
	//   val, ok := ctx.Get("foo")
	// }
	Get(key string) (any, bool)

	GetResponse() *response
	SetResponseHeader(key, value string)

	// get raw request
	GetRequest() *http.Request

	// get request one header param
	GetRequestHeaderParam(key string) string

	// get request any header params
	ExtractRequestHeaderParam(key string) []string

	// return 204 & nil
	NoContent() error

	// set request id to store
	// and return value
	RequestId() string

	// get remoteAddr
	GetRemoteIP() (string, error)

	RegisterTrustIPRange(ranges *net.IPNet)
	GetIPFromXFFHeader() (string, error)

	// get requested ip
	//   1. Get from XFF
	//   1. Get from RealIP
	//   1. Get from GetRemoteIp
	RealIP() (string, error)

	// reset context
	Reset(w http.ResponseWriter, r *http.Request)

	// set logger
	//
	// you can get logger in your handler
	// func main() {
	//   p := poteto.New()
	//   p.SetLogger(<your logger>)
	// }
	// in your handler
	// func handler (ctx poteto.Context) error {
	//   logger := ctx.Logger()
	// }
	SetLogger(logger any)

	// get logger
	Logger() any
}

func NewContext

func NewContext(w http.ResponseWriter, r *http.Request) Context

type HandlerFunc

type HandlerFunc func(ctx Context) error

type HttpError

type HttpError interface {
	Error() string
	SetInternalError(err error)
	Unwrap() error
}

type HttpErrorHandler

type HttpErrorHandler interface {
	HandleHttpError(err error, ctx Context)
}

type HttpParam

type HttpParam interface {
	GetParam(paramType, key string) (string, bool)
	AddParam(paramType string, paramUnit ParamUnit)
	JsonSerialize() ([]byte, error)
	// contains filtered or unexported methods
}

func NewHttpParam

func NewHttpParam() HttpParam

type IPHandler

type IPHandler interface {
	SetIsTrustPrivateIP(flag bool)
	RegisterTrustIPRange(ranges *net.IPNet)
	CanTrust(ip net.IP) bool
	GetIPFromXFFHeader(ctx Context) (string, error)
	GetRemoteIP(ctx Context) (string, error)
	RealIP(ctx Context) (string, error)
}

type Leaf

type Leaf interface {
	// internal call Poteto.Combine w/ base path
	Register(middlewares ...MiddlewareFunc) *middlewareTree

	// internal call Poteto.GET w/ base path
	GET(addPath string, handler HandlerFunc) error

	// internal call Poteto.POST w/ base path
	POST(addPath string, handler HandlerFunc) error

	// internal call Poteto.PUT w/ base path
	PUT(addPath string, handler HandlerFunc) error

	// internal call Poteto.PATCH w/ base path
	PATCH(path string, handler HandlerFunc) error

	// internal call Poteto.DELETE w/ base path
	DELETE(addPath string, handler HandlerFunc) error

	// internal call Poteto.HEAD w/ base path
	HEAD(path string, handler HandlerFunc) error

	// internal call Poteto.OPTIONS w/ base path
	OPTIONS(path string, handler HandlerFunc) error

	// internal call Poteto.TRACE w/ base path
	TRACE(path string, handler HandlerFunc) error

	// internal call Poteto.CONNECT w/ base path
	CONNECT(path string, handler HandlerFunc) error
}

func NewLeaf

func NewLeaf(poteto Poteto, basePath string) Leaf

type LeafHandler

type LeafHandler func(leaf Leaf)

type MiddlewareFunc

type MiddlewareFunc func(next HandlerFunc) HandlerFunc

type MiddlewareTree

type MiddlewareTree interface {
	SearchMiddlewares(pattern string) []MiddlewareFunc
	Insert(pattern string, middlewares ...MiddlewareFunc) *middlewareTree
	Register(middlewares ...MiddlewareFunc)
}

func NewMiddlewareTree

func NewMiddlewareTree() MiddlewareTree

type ParamUnit

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

type Poteto

type Poteto interface {
	// If requested, call this
	ServeHTTP(w http.ResponseWriter, r *http.Request)
	Run(addr string) error
	RunTLS(addr string, cert, key []byte) error
	Stop(ctx stdContext.Context) error

	Register(middlewares ...MiddlewareFunc)
	Combine(pattern string, middlewares ...MiddlewareFunc) *middlewareTree
	SetLogger(logger any)
	Leaf(basePath string, handler LeafHandler)

	// workflow is a function that is executed when the server starts | end
	// - constant.StartUpWorkflow: "startUp"
	//  - This is a workflow that is executed when the server starts
	RegisterWorkflow(workflowType string, priority uint, workflow WorkflowFunc)

	GET(path string, handler HandlerFunc) error
	POST(path string, handler HandlerFunc) error
	PUT(path string, handler HandlerFunc) error
	PATCH(path string, handler HandlerFunc) error
	DELETE(path string, handler HandlerFunc) error
	HEAD(path string, handler HandlerFunc) error
	OPTIONS(path string, handler HandlerFunc) error
	TRACE(path string, handler HandlerFunc) error
	CONNECT(path string, handler HandlerFunc) error

	// poteto.Play make ut w/o server
	// EX:
	//  p := poteto.New()
	//  p.GET("/users", func(ctx poteto.Context) error {
	//    return ctx.JSON(http.StatusOK, map[string]string{
	//      "id":   "1",
	//      "name": "tester",
	//    })
	//  })
	//  res := p.Play(http.MethodGet, "/users")
	//  resBodyStr := res.Body.String
	//  // => {"id":"1","name":"tester"}
	Play(method, path string, body ...string) *httptest.ResponseRecorder

	// check if target route has handler.
	// func main() {
	//   p := poteto.New()
	//
	//   p.GET("/users", handler)
	//
	//   ok := p.Check(http.MethodGet, "/users") // -> return true
	//   ng := p.Check(http.MethodPost, "/users") // -> return false
	// }
	Check(method, path string) bool

	// Chain Handler & middleware
	//
	// This is high-readability on just-one-path middleware
	//
	// func main() {
	//   p := poteto.New()
	//
	//   p.GET(
	//     "/users",
	//     p.Chain(
	//       middleware1,
	//       middleware2,
	//     )(handler)
	//   )
	// }
	Chain(middlewares ...MiddlewareFunc) func(HandlerFunc) HandlerFunc
	// contains filtered or unexported methods
}

func New

func New() Poteto

func NewWithOption

func NewWithOption(option PotetoOption) Poteto

type PotetoOption

type PotetoOption struct {
	WithRequestId   bool   `yaml:"with_request_id" env:"WITH_REQUEST_ID" envDefault:"true"`
	DebugMode       bool   `yaml:"debug_mode" env:"DEBUG_MODE" envDefault:"false"`
	ListenerNetwork string `yaml:"listener_network" env:"LISTENER_NETWORK" envDefault:"tcp"`
}

ENV:

WITH_REQUEST_ID: bool [true]
DEBUG_MODE: bool [false]
LISTENER_NETWORK: string [tcp]

type PotetoWorkflows added in v1.3.0

type PotetoWorkflows interface {
	RegisterWorkflow(workflowType string, priority uint, workflow WorkflowFunc)
	ApplyStartUpWorkflows() error
}

workflow is a function that is executed when the server starts | end - constant.StartUpWorkflow: "startUp"

  • This is a workflow that is executed when the server starts

func NewPotetoWorkflows added in v1.3.0

func NewPotetoWorkflows() PotetoWorkflows

type Response

type Response interface {
	/*
		Write statusCode to http.ResponseWriter
	*/
	WriteHeader(code int)

	/*
		Write to http.ResponseWriter
	*/
	Write(b []byte) (int, error)

	/*
		Set status code
	*/
	SetStatus(code int)

	/*
		return http.ResponseWriter.Header()
	*/
	Header() http.Header

	/*
		Set Header if key doesn't include
	*/
	SetHeader(key, value string)

	/*
		Internal call http.ResponseWriter.Header().Add
	*/
	AddHeader(key, value string)

	/*
		fullfil interface for (making) responseController

		you can assign to responseController

		func hoge() {
			res := NewResponse(w)
			rc := http.NewResponseController(res)
		}

		https://go.dev/src/net/http/responsecontroller.go
	*/
	Unwrap() http.ResponseWriter
}

func NewResponse

func NewResponse(w http.ResponseWriter) Response

type Route

type Route interface {
	Search(path string) (*route, []ParamUnit)
	Insert(path string, handler HandlerFunc)

	GetHandler() HandlerFunc
}

func NewRoute

func NewRoute() Route

type Router

type Router interface {

	/*
		Register GET method Route

		Trim Suffix "/"
		EX: "/users/" -> "/users"
	*/
	GET(path string, handler HandlerFunc) error

	/*
		Register POST method Route

		Trim Suffix "/"
		EX: "/users/" -> "/users"
	*/
	POST(path string, handler HandlerFunc) error

	/*
		Register PUT method Route

		Trim Suffix "/"
		EX: "/users/" -> "/users"
	*/
	PUT(path string, handler HandlerFunc) error

	/*
		Register PATCH method Route

		Trim Suffix "/"
		EX: "/users/" -> "/users"
	*/
	PATCH(path string, handler HandlerFunc) error

	/*
		Register DELETE method Route

		Trim Suffix "/"
		EX: "/users/" -> "/users"
	*/
	DELETE(path string, handler HandlerFunc) error

	/*
		Register HEAD method Route

		Trim Suffix "/"
		EX: "/users/" -> "/users"
	*/
	HEAD(path string, handler HandlerFunc) error

	/*
		Register OPTIONS method Route

		Trim Suffix "/"
		EX: "/users/" -> "/users"
	*/
	OPTIONS(path string, handler HandlerFunc) error

	/*
		Register TRACE method Route

		Trim Suffix "/"
		EX: "/users/" -> "/users"
	*/
	TRACE(path string, handler HandlerFunc) error

	/*
		Register CONNECT method Route

		Trim Suffix "/"
		EX: "/users/" -> "/users"
	*/
	CONNECT(path string, handler HandlerFunc) error

	GetRoutesByMethod(method string) *route
	// contains filtered or unexported methods
}

func NewRouter

func NewRouter() Router

Router Provides Radix-Tree Routing

O(logN) ~ N

Supports standard(net/http) methods GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, TRACE, CONNECT

You can use only Router of course.

type UnitWorkflow added in v1.3.0

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

type WorkflowFunc added in v1.3.0

type WorkflowFunc func() error

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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