poteto

package module
v1.12.1 Latest Latest
Warning

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

Go to latest
Published: Jun 9, 2025 License: MIT Imports: 21 Imported by: 1

README

Poteto

Simple Web Framework of GoLang

go get -u github.com/poteto-go/poteto@latest

If you try latest experiment version

https://github.com/poteto-go/poteto/blob/main/EXPERIMENT.md

go get -u github.com/poteto-go/poteto@exp<version>

Deep Wiki

https://deepwiki.com/poteto-go/poteto

Quick Start

func main() {
	p := poteto.New()
	p.Register(middleware.CORSWithConfig(middleware.CORSConfig{
		AllowOrigins: []string{"*"},
		AllowMethods: []string{http.MethodGet, http.MethodPut, http.MethodPost, http.MethodDelete},
	}))

	p.GET("/", func(ctx poteto.Context) error {
		return ctx.JSON(http.StatusOK, map[string]string{
			"message": "Hello World",
		})
	})

	userApi := poteto.Api("/users", func(leaf poteto.Leaf) {
		leaf.GET("/:id", func(ctx poteto.Context) error {
			id, _ := p.PathParam("id")
			return ctx.JSON(http.StatusOK, map[string]string{
				"id": id,
			})
		})
	})

	p.AddApi(userApi)

	p.Run("3000")
}
UT

[!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"}
}
middleware

use poteto-extensions/middleware

go get github.com/poteto-go/poteto-extensions

OIDC

poteto provides easily oidc middleware.

  • verify signature
  • jwt schema (if idp google).
func main() {
  p := poteto.New()

  oidcConfig := middleware.OidcConfig {
	  Idp: "google",
		ContextKey: "googleToken",
    CacheMode: true,
    JwksUrl: "https://www.googleapis.com/oauth2/v3/certs",
    CachedVerifyTokenSignature: oidc.CachedVerifyTokenSignature,
  }
  p.Register(
    middleware.OidcWithConfig(
      oidcConfig,
    )
  )

  p.POST("/login", func(ctx poteto.Context) error {
      var claims oidc.GoogleOidcClaims
      token, _ := ctx.Get("googleToken")
      json.Unmarshal(token.([]byte), &claims)
      ...
      return ctx.JSON(200, map[string]string{"message": "success"})
  })
}

Example App For Poteto

TODO

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

Documentation

Index

Constants

View Source

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

Variables

This section is empty.

Functions

func Api added in v1.7.0

func Api(basePath string, handler LeafHandler) *poteto

func DefaultErrorHandler added in v1.10.2

func DefaultErrorHandler(err error, ctx Context)

This is defaultErrorHandler

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 CONNECT added in v1.11.0

type CONNECT struct{}

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

	// TODO: delete > 2.0
	SetParam(paramType string, paramUnit ParamUnit)

	SetQueryParam(queryParams url.Values)

	// 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 DELETE added in v1.11.0

type DELETE struct{}

type ErrorHandlerFunc added in v1.10.2

type ErrorHandlerFunc func(err error, ctx Context)

type GET added in v1.11.0

type GET struct{}
type HEAD struct{}

type HTTPMethod added in v1.11.0

type HTTPMethod interface {
	GET | POST | PUT | PATCH | DELETE | HEAD | OPTIONS | TRACE | CONNECT
}

type HandlerFunc

type HandlerFunc func(ctx Context) error

type HttpError

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

type HttpParam

type HttpParam interface {

	// TODO: delete > 2.0
	GetParam(paramType, key string) (string, bool)
	GetPathParam(key string) (string, bool)
	GetQueryParam(key string) (string, bool)
	// TODO: delete > 2.0
	AddParam(paramType string, paramUnit ParamUnit)
	AddPathParam(paramUnit ParamUnit)
	AddQueryParam(paramUnit ParamUnit)
	JsonSerialize() ([]byte, error)

	// reset params
	Reset()
	// 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)

	// DFS route & return linearRouter
	//
	// []{
	//   path: string,
	//   handler: MiddlewareFunc,
	// }
	DFS() []middlewareLinear
	// contains filtered or unexported methods
}

func NewMiddlewareTree

func NewMiddlewareTree() MiddlewareTree

type OPTIONS added in v1.11.0

type OPTIONS struct{}

type PATCH added in v1.11.0

type PATCH struct{}

type POST added in v1.11.0

type POST struct{}

type PUT added in v1.11.0

type PUT struct{}

type ParamUnit

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

type Poteto

type Poteto interface {
	Router() *router
	MiddlewareTree() *middlewareTree

	// 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 makes router group
	// You can make your router clear
	// with middlewares
	Leaf(basePath string, handler LeafHandler)

	// add router & middleware tree from api (Poteto)
	AddApi(api Poteto) 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
	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

	SetErrorHandler(handler ErrorHandlerFunc)
	// 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

	/*
		reset response
	*/
	Reset(w 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)

	// DFS route & return linearRouter
	//
	// []{
	//   path: string,
	//   handler: HandlerFunc,
	// }
	DFS() []routeLinear

	GetHandler() HandlerFunc
	// contains filtered or unexported methods
}

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

	// DFS route & return linearRouter by method
	//
	// []{
	//   path: string,
	//   handler: HandlerFunc,
	// }
	DFS(method string) []routeLinear

	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 TRACE added in v1.11.0

type TRACE struct{}

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