Skip to content

nktknshn/go-ergo-handler

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

40 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

go-ergo-handler

Ergonomic HTTP handlers builder for Go.

About

This library can help you build type-safe HTTP-handlers from reusable middlewares in a concise and declarative way.

Concept

You define parsers (essentially middleware functions) that extract values from http.Request and place them into the context. Attaching a parser provides a typed interface for retrieving these values from the context, ensuring that values are not accessed without the required parser being attached. Before a handler is invoked, the chain of parsers is executed. If any parser fails, an error is returned, and the handler is not called. This process is similar to Either monad chaining in Functional Programming. By the time the handler is invoked, you can be confident that all required values have been successfully parsed and validated (if necessary). Go generics provide a type-safe and ergonomic experience, with all type casting handled internally.

Installation

go get github.com/nktknshn/go-ergo-handler

Example

import geh "github.com/nktknshn/go-ergo-handler"

type payloadType struct {
	Title string `json:"title"`
	Price int    `json:"price"`
}

func (p payloadType) Validate() error {
	if p.Title == "" {
		return errors.New("empty book title")
	}
	if p.Price <= 0 {
		return errors.New("invalid book price")
	}
	return nil
}

type paramBookIDType int

func (p paramBookIDType) Parse(ctx context.Context, v string) (paramBookIDType, error) {
	vint, err := strconv.Atoi(v)
	if err != nil {
		return 0, errors.New("book_id is not a number")
	}
	return paramBookIDType(vint), nil
}

func (p paramBookIDType) Validate() error {
	if p <= 0 {
		return errors.New("book_id is not a positive number")
	}
	return nil
}

var (
	paramBookID    = geh.RouterParamWithParser[paramBookIDType]("book_id")
	payloadBook    = geh.Payload[payloadType]()
	paramUnpublish = geh.QueryParamMaybe("unpublish", geh.IgnoreContext(strconv.ParseBool))
)

func makeHttpHandler(useCase interface {
	UpdateBook(ctx context.Context, bookID int, title string, price int, unpublish bool) error
}) http.Handler {
	var (
		builder   = geh.New()
		bookID    = paramBookID.Attach(builder)
		payload   = payloadBook.Attach(builder)
		unpublish = paramUnpublish.Attach(builder)
	)

	return builder.BuildHandlerWrapped(func(w http.ResponseWriter, r *http.Request) (any, error) {
		// all values are parsed and validated at this point
		bid := bookID.Get(r)
		pl := payload.Get(r)
		unp := unpublish.GetDefault(r, false)
		err := useCase.UpdateBook(r.Context(), int(bid), pl.Title, pl.Price, unp)
		if err != nil {
			return nil, err
		}
		return nil, nil
	})
}

Usage

Example project utilizing the library for a handlers layer.

https://github.com/nktknshn/go-ergo-handler-example

https://github.com/nktknshn/go-ergo-handler-example/tree/master/internal/adapters/http_adapter/handlers

About

Ergonomic HTTP handlers builder for Go.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages