expect-digital / go-request

MIT License
1 stars 0 forks source link

request GoDoc GitHub Workflow Status GitHub

godoc go.expect.digital/request

Package request simplifies the decoding of HTTP requests (REST API) into Go structs for easier consumption. It implements decoding based on the OpenAPI 3.1 specification.

In general, it is better to use code generation from the API specification, e.g. OpenAPI spec to a server code in Golang. However, it's not always possible due to certain constraints.

Key Features:

Reading path value

By default, the Decoder reads a path value using request.PathValue.

Declare once and re-use in handlers.

net/http

package main

import (
  "net/http"

  "go.expect.digital/request"
)

func main() {
  http.HandleFunc("/{id}", func (w http.ResponseWriter, r *http.Request) {
    var req struct {
      ID int `path:"id"`
    }

    if err := request.Decode(r, &req); err != nil {
      return
    }
  })

  log.Fatal(http.ListenAndServe(":8080", nil))
}

Chi

package main

import (
    "log"
    "net/http"

    "github.com/go-chi/chi/v5"
    "go.expect.digital/request"
)

func main() {
    decode := request.NewDecoder(
        request.PathValue(
            func(r *http.Request, name string) string {
                return chi.URLParam(r, name)
            },
        ),
    ).Decode

    r := chi.NewRouter()

    r.Get("/{id}", func(w http.ResponseWriter, r *http.Request) {
        var req struct {
            ID int `path:"id"`
        }

        if err := decode(r, &req); err != nil {
            return
        }
    })

    log.Fatal(http.ListenAndServe("127.0.0.1:8080", r))
}

Gorilla

package main

import (
    "log"
    "net/http"

    "github.com/gorilla/mux"
    "go.expect.digital/request"
)

func main() {
    decode := request.NewDecoder(
        request.PathValue(func(r *http.Request, name string) string {
            return mux.Vars(r)[name]
        }),
    ).Decode

    r := mux.NewRouter()

    r.Path("/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        var req struct {
            ID int `path:"id"`
        }

        if err := decode(r, &req); err != nil {
            return
        }
    })

    log.Fatal(http.ListenAndServe("127.0.0.1:8080", r))
}

Gin

We advise using Gin data binding implementation.

Example of using the package in Gin:

package main

import (
    "log"
    "net/http"

    "github.com/gin-gonic/gin"
    "go.expect.digital/request"
)

func main() {
    r := gin.Default()

    r.GET("/:id", func(c *gin.Context) {
        decode := request.NewDecoder(
            request.PathValue(func(r *http.Request, name string) string {
                return c.Param(name)
            }),
        ).Decode

        var req struct {
            ID int `path:"id"`
        }

        if err := decode(c.Request, &req); err != nil {
            return
        }
    })

    log.Fatal(r.Run())
}