diff --git a/examples/simple/main.go b/examples/simple/main.go index 45d0bdf..de6cace 100644 --- a/examples/simple/main.go +++ b/examples/simple/main.go @@ -1,69 +1,50 @@ package main import ( - "context" + "fmt" "net/http" "github.com/go-chi/chi" "github.com/go-chi/chi/middleware" - validation "github.com/go-ozzo/ozzo-validation/v4" - "github.com/go-ozzo/ozzo-validation/v4/is" "github.com/expectedsh/kcd" ) -const idContext = "id" - func main() { r := chi.NewRouter() r.Use(middleware.RequestID) - // You can configure kcd with kcd.Config.{ ErrorHook, + // You can configure kcd with kcd.Config. ErrorHook, // RenderHook, // BindHook, // ValidateHook, // LogHook, // StringsExtractors, - // ValueExtractors } - - r.Use(func(handler http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - ctx = context.WithValue(ctx, idContext, 12345) - handler.ServeHTTP(w, r) - }) - }) + // ValueExtractors - r.Get("/{name}", kcd.Handler(SuperShinyHandler, http.StatusOK)) + r.Get("/{name}", kcd.Handler(YourHttpHandler, http.StatusOK)) + // ^ Here the magic happen this is the only thing you need + // to do. Adding kcd.Handler(your handler) _ = http.ListenAndServe(":3000", r) } // CreateCustomerInput is an example of input for an http request. type CreateCustomerInput struct { - Name string `path:"name"` - Emails []string `query:"emails" exploder:","` - ContextualID *struct { - ID int `ctx:"id" default:"12345"` - } + Name string `path:"name"` + Emails []string `query:"emails" exploder:","` } -// Validate is the function that will be called before calling your shiny handler. -func (c CreateCustomerInput) Validate() error { - return validation.ValidateStruct(&c, - validation.Field(&c.Name, validation.Required, validation.Length(5, 20)), - validation.Field(&c.Emails, validation.Each(is.Email)), - validation.Field(&c.ContextualID, validation.Required), - ) +// CustomerOutput is the output type of your handler it contain the input for simplicity. +type CustomerOutput struct { + Name string `json:"name"` } -// Customer is the output type of your handler it contain the input for simplicity. -type Customer struct { - CreateCustomerInput -} - -// SuperShinyHandler is your http handler but in a shiny version. -func SuperShinyHandler(in *CreateCustomerInput) (Customer, error) { +// YourHttpHandler is your http handler but in a shiny version. +// You can add *http.ResponseWriter or http.Request in params if you want. +func YourHttpHandler(in *CreateCustomerInput) (CustomerOutput, error) { // do some stuff here - return Customer{*in}, nil + fmt.Printf("%+v", in) + + return CustomerOutput{Name: in.Name}, nil } diff --git a/readme.md b/readme.md index 372947e..5a45560 100644 --- a/readme.md +++ b/readme.md @@ -14,33 +14,77 @@ ## :stars: KCD -KCD is a grandiose REST helper that wrap your shiny handler into a classic http handler. It manage all you want for building REST services. +KCD is a grandiose REST helper that wrap your shiny handler into a classic http handler. +It manages all you want for building REST services. -This library is **opinionated** by default but **fully customizable** which mean it uses some other libraries like Chi for instance. KCD is modular so each pieces of the code that rely on a specific library can be changed. +This library is **opinionated** by default but **customizable** which mean it uses some other libraries like Chi, Logrus... +KCD is modular so each pieces of the code that rely on a specific library can be changed. -## :rocket: What KCD does exactly +## :rocket: QuickStart -Okay so KCD will wrap your cool handler into a http handler. The magic happen with this function: +```go +package main -`kcd.Handler(YourShinyHandler, http.StatusOK)` (which returns a http.HandlerFunc) +import ( + "fmt" + "net/http" -Your handler is the `YourShinyHandler` parameter, it accepts: -```go -func([response http.ResponseWriter], [request *http.Request], [input object ptr]) ([output object], error) + "github.com/go-chi/chi" + "github.com/go-chi/chi/middleware" + + "github.com/expectedsh/kcd" +) + +func main() { + r := chi.NewRouter() + r.Use(middleware.RequestID) + + // You can configure kcd with kcd.Config + + r.Get("/{name}", kcd.Handler(YourHttpHandler, http.StatusOK)) + // ^ Here the magic happen this is the only thing you need + // to do. Adding kcd.Handler(your handler) + _ = http.ListenAndServe(":3000", r) +} + +// CreateCustomerInput is an example of input for an http request. +type CreateCustomerInput struct { + Name string `path:"name"` // you can extract value from: 'path', 'query', 'header', 'ctx' + Emails []string `query:"emails" exploder:","` // exploder split value with the char specified +} + +// CustomerOutput is the output type of your handler it contain the input for simplicity. +type CustomerOutput struct { + Name string `json:"name"` +} + +// YourHttpHandler is your http handler but in a shiny version. +// You can add *http.ResponseWriter or http.Request in params if you want. +func YourHttpHandler(in *CreateCustomerInput) (CustomerOutput, error) { + // do some stuff here + fmt.Printf("%+v", in) + + return CustomerOutput{Name: in.Name}, nil +} ``` - -The only parameter in your shiny handler that is required is the returned error. -**If there are any errors at some point KCD will call the [error hook](pkg/hook/error.go) to provide a REST generic error**. +You can test this code by using curl `curl localhost:3000/supername?emails=alexis@gmail.com,remi@gmail.com` + +## :check: Validation -1. If there is a custom input parameter (a pointer to a structure) it will: - 1. Run all [extractors](pkg/extractor) to extract values from the request into the input (query parameters, path, header, default value ...) - 2. Run the JSON body [bind hook](pkg/hook/bind.go) - 3. Validate the input through the [validate hook](pkg/hook/validate.go) -3. If all is good it will then call your shiny handler with all required arguments -4. Then if there is an output parameter it will call the [render hook](pkg/hook/render.go) +KCD can validate your input by using a fork of [ozzo-validation](https://github.com/expectedsh/ozzo-validation). -That's all. Well that's it, that's all you should have done if you didn't have KCD. +Your input need to implement Validatable or ValidatableWithContext. + +```go +// Validate is the function that will be called before calling your shiny handler. +func (c CreateCustomerInput) Validate() error { + return validation.ValidateStruct(&c, + validation.Field(&c.Name, validation.Required, validation.Length(5, 20)), + validation.Field(&c.Emails, validation.Each(is.Email)), + ) +} +``` ## :coffee: Benefits