Skip to content

Commit

Permalink
feat: remove chi dependency (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
rluders authored Jan 29, 2025
1 parent 3363c15 commit f081d27
Show file tree
Hide file tree
Showing 15 changed files with 458 additions and 220 deletions.
58 changes: 7 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,69 +12,25 @@ cleaner, more maintainable code with reduced boilerplate.
- **Modular Design:** Each component (Request, Validation, Response) can be used independently,
enhancing testability and flexibility.

> **Note:** Currently it only supports Chi.
### Supported routers

- Gorilla MUX
- Chi
- Go Standard
- ...maybe more? Submit a PR with an example.

## Installation

To install **httpsuite**, run:

```
go get github.com/rluders/httpsuite
go get github.com/rluders/httpsuite/v2
```

## Usage

### Request Parsing with URL Parameters

Easily parse incoming requests and set URL parameters:

```go
package main

import (
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/rluders/httpsuite"
"log"
"net/http"
)

type SampleRequest struct {
Name string `json:"name" validate:"required,min=3"`
Age int `json:"age" validate:"required,min=1"`
}

func (r *SampleRequest) SetParam(fieldName, value string) error {
switch fieldName {
case "name":
r.Name = value
}
return nil
}

func main() {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)

r.Post("/submit/{name}", func(w http.ResponseWriter, r *http.Request) {
// Step 1: Parse the request and validate it
req, err := httpsuite.ParseRequest[*SampleRequest](w, r, "name")
if err != nil {
log.Printf("Error parsing or validating request: %v", err)
return
}

// Step 2: Send a success response
httpsuite.SendResponse[SampleRequest](w, http.StatusOK, *req, nil, nil)
})

log.Println("Starting server on :8080")
http.ListenAndServe(":8080", r)
}

```

Check out the [example folder for a complete project](./examples) demonstrating how to integrate **httpsuite** into
your Go microservices.

Expand Down
20 changes: 20 additions & 0 deletions examples/chi/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module chi_example

go 1.23

require (
github.com/go-chi/chi/v5 v5.2.0
github.com/rluders/httpsuite/v2 v2.0.0
)

require (
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.24.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
)
30 changes: 30 additions & 0 deletions examples/chi/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0=
github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg=
github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
77 changes: 77 additions & 0 deletions examples/chi/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package main

import (
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/rluders/httpsuite/v2"
"log"
"net/http"
"strconv"
)

type SampleRequest struct {
ID int `json:"id" validate:"required"`
Name string `json:"name" validate:"required,min=3"`
Age int `json:"age" validate:"required,min=1"`
}

type SampleResponse struct {
ID int `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
}

func (r *SampleRequest) SetParam(fieldName, value string) error {
switch fieldName {
case "id":
id, err := strconv.Atoi(value)
if err != nil {
return err
}
r.ID = id
}
return nil
}

func ChiParamExtractor(r *http.Request, key string) string {
return chi.URLParam(r, key)
}

// You can test it using:
//
// curl -X POST http://localhost:8080/submit/123 \
// -H "Content-Type: application/json" \
// -d '{"name": "John Doe", "age": 30}'
//
// And you should get:
//
// {"data":{"id":123,"name":"John Doe","age":30}}
func main() {
// Creating the router with Chi
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)

// Define the endpoint POST
r.Post("/submit/{id}", func(w http.ResponseWriter, r *http.Request) {
// Using the function for parameter extraction to the ParseRequest
req, err := httpsuite.ParseRequest[*SampleRequest](w, r, ChiParamExtractor, "id")
if err != nil {
log.Printf("Error parsing or validating request: %v", err)
return
}

resp := &SampleResponse{
ID: req.ID,
Name: req.Name,
Age: req.Age,
}

// Sending success response
httpsuite.SendResponse[SampleResponse](w, http.StatusOK, *resp, nil, nil)
})

// Starting the server
log.Println("Starting server on :8080")
http.ListenAndServe(":8080", r)
}
20 changes: 20 additions & 0 deletions examples/gorillamux/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module gorillamux_example

go 1.23

require (
github.com/gorilla/mux v1.8.1
github.com/rluders/httpsuite/v2 v2.0.0
)

require (
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.24.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
)
30 changes: 30 additions & 0 deletions examples/gorillamux/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg=
github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
66 changes: 66 additions & 0 deletions examples/gorillamux/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package main

import (
"github.com/gorilla/mux"
"github.com/rluders/httpsuite/v2"
"log"
"net/http"
"strconv"
)

type SampleRequest struct {
ID int `json:"id" validate:"required"`
Name string `json:"name" validate:"required,min=3"`
Age int `json:"age" validate:"required,min=1"`
}

type SampleResponse struct {
ID int `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
}

func (r *SampleRequest) SetParam(fieldName, value string) error {
switch fieldName {
case "id":
id, err := strconv.Atoi(value)
if err != nil {
return err
}
r.ID = id
}
return nil
}

func GorillaMuxParamExtractor(r *http.Request, key string) string {
return mux.Vars(r)[key] // Extracts parameter using Gorilla Mux
}

// Test the server using:
// curl -X POST http://localhost:8080/submit/123 -H "Content-Type: application/json" -d '{"name": "John Doe", "age": 30}'
func main() {
// Creating the router with Gorilla Mux
r := mux.NewRouter()

r.HandleFunc("/submit/{id}", func(w http.ResponseWriter, r *http.Request) {
// Using the function for parameter extraction to the ParseRequest
req, err := httpsuite.ParseRequest[*SampleRequest](w, r, GorillaMuxParamExtractor, "id")
if err != nil {
log.Printf("Error parsing or validating request: %v", err)
return
}

resp := &SampleResponse{
ID: req.ID,
Name: req.Name,
Age: req.Age,
}

// Sending success response
httpsuite.SendResponse[SampleResponse](w, http.StatusOK, *resp, nil, nil)
}).Methods("POST")

// Starting the server
log.Println("Starting server on :8080")
http.ListenAndServe(":8080", r)
}
43 changes: 0 additions & 43 deletions examples/main.go

This file was deleted.

17 changes: 17 additions & 0 deletions examples/stdmux/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module stdmux_example

go 1.23

require github.com/rluders/httpsuite/v2 v2.0.0

require (
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.24.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
)
Loading

0 comments on commit f081d27

Please sign in to comment.