Efficient way to make REST handlers in Go (without repeating code)? - api

Currently I have too much repeated code for the handlers:
type GuestMux struct {
http.ServeMux
}
func main() {
guestMux := NewGuestMux()
http.ListenAndServe(":3001", guestMux)
}
func NewGuestMux() *GuestMux {
var guestMux = &GuestMux{}
guestMux.HandleFunc("/guest/createguest", createGuestHandler)
guestMux.HandleFunc("/guest/updateguest", updateGuestHandler)
guestMux.HandleFunc("/guest/getguest", getGuestHandler)
return guestMux
}
func createGuestHandler(w http.ResponseWriter, r *http.Request) {
var createGuestReq CreateGuestRequest
reqBody, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
err = json.Unmarshal(reqBody, &createGuestReq)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusBadRequest)
return
}
resp, err := CreateGuest(&createGuestReq)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}
func updateGuestHandler(w http.ResponseWriter, r *http.Request) {
var updateGuestReq UpdateGuestRequest
reqBody, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
err = json.Unmarshal(reqBody, &updateGuestReq)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusBadRequest)
return
}
resp, err := UpdateGuest(&updateGuestReq)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}
func getGuestHandler(w http.ResponseWriter, r *http.Request) {
// almost the same as above two handlers, just different method to call and
// its parameter type
...
}
Is there any nicer way to write the handlers createGuestHandler, updateGuestHandler and getGuestHandler instead of repeating similar code blocks three times. I guess I can use interface but am not sure how to write that. I have about 20 handlers so the repeating code does not seem really maintainable.
//stackoverflow does not allow question with too much code over details so... details here, details there, even more details...//

You can move the common logic to a separate function, and pass everything to it that is specific in each handler.
Let's assume you have these types and functions:
type CreateGuestRequest struct{}
type UpdateGuestRequest struct{}
type CreateGuestResponse struct{}
type UpdateGuestResponse struct{}
func CreateGuest(v *CreateGuestRequest) (resp *CreateGuestResponse, err error) {
return nil, nil
}
func UpdateGuest(v *UpdateGuestRequest) (resp *UpdateGuestResponse, err error) {
return nil, nil
}
With generics allowed
If generics are allowed, you can factor all code out of handlers:
func handle[Req any, Resp any](w http.ResponseWriter, r *http.Request, logicFunc func(dst Req) (Resp, error)) {
var dst Req
if err := json.NewDecoder(r.Body).Decode(&dst); err != nil {
log.Printf("Decoding body failed: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
resp, err := logicFunc(dst)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Printf("Encoding response failed: %v", err)
}
}
func createGuestHandler(w http.ResponseWriter, r *http.Request) {
handle(w, r, CreateGuest)
}
func updateGuestHandler(w http.ResponseWriter, r *http.Request) {
handle(w, r, UpdateGuest)
}
As you can see, all handler implementations are just a single line! We can even get rid of the handler functions now, as we can create a handler from a logic function (like CreateGuest(), UpdateGuest()).
This is how it would look like:
func createHandler[Req any, Resp any](logicFunc func(dst Req) (Resp, error)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var dst Req
if err := json.NewDecoder(r.Body).Decode(&dst); err != nil {
log.Printf("Decoding body failed: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
resp, err := logicFunc(dst)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Printf("Encoding response failed: %v", err)
}
}
}
And using it:
func NewGuestMux() *GuestMux {
var guestMux = &GuestMux{}
guestMux.HandleFunc("/guest/createguest", createHandler(CreateGuest))
guestMux.HandleFunc("/guest/updateguest", createHandler(UpdateGuest))
return guestMux
}
Without generics
This solution does not use generics (and works with old Go versions too).
func handle(w http.ResponseWriter, r *http.Request, dst interface{}, logicFunc func() (interface{}, error)) {
if err := json.NewDecoder(r.Body).Decode(dst); err != nil {
log.Printf("Decoding body failed: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
resp, err := logicFunc()
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Printf("Encoding response failed: %v", err)
}
}
func createGuestHandler(w http.ResponseWriter, r *http.Request) {
var createGuestReq CreateGuestRequest
handle(w, r, &createGuestReq, func() (interface{}, error) {
return CreateGuest(&createGuestReq)
})
}
func updateGuestHandler(w http.ResponseWriter, r *http.Request) {
var updateGuestReq UpdateGuestRequest
handle(w, r, &updateGuestReq, func() (interface{}, error) {
return UpdateGuest(&updateGuestReq)
})
}

There are many ways to avoid repetition here, for example, you could use a decorator pattern, where you can define how to decode/encode and other steps that doesn't include your business logic.
You can check two interesting approaches:
One is from Mat: https://pace.dev/blog/2018/05/09/how-I-write-http-services-after-eight-years.html
The other one is the go-kit package (you can check it out on github), but I recommend you to checkout the idea on how to compose decorators instead of installing the library, could be an overkill for your implematation.

Typically REST APIs have just /guest endpoint with single handler that decides what to do based on HTTP method:
POST to create
GET to retrieve
PUT to update the entire record
PATCH to update certain fields
You can look at r.Method inside your handler and decide what code to run based on that.
If you are bound to interface shown in your question you can e.g. wrap handler to an anonymous function with expected interface and make it accept an additional argument to decide what to do like this:
guestMux.HandleFunc("/guest/createguest", func(w http.ResponseWriter, r *http.Request) {
guestHandler(r, w, CREATE)
})
guestMux.HandleFunc("/guest/updateguest", func(w http.ResponseWriter, r *http.Request) {
guestHandler(r, w, UPDATE)
})
...
(where CREATE and UPDATE are some sort of flags that tell guestHandler() what it should do)

I suggest to have a look to go-kit.
It's mainly designed to create services using Hexagonal architecture. It brings a lot of utility functions to avoid repeated code and focus on the business logic.
It has a lot of functionality that may not need but since it's a toolkit (and not a complete framework) you're free to use only the parts that you need.
Examples are also easy to follow.

I have these utility functions : decodeJsonBody, respondJson that I use to simplify response, without adding too much complexity. I wrap it in the Response struct for sending client side error details.
type Response struct {
Data interface{} `json:"data"`
Errors interface{} `json:"errors"`
}
func respondJson(w http.ResponseWriter, data interface{}, err error) {
w.Header().Set("Content-Type", "application/json")
if err != nil {
w.WriteHeader(http.StatusBadRequest)
err = json.NewEncoder(w).Encode(Response{
Errors: err.Error(),
})
return
}
err = json.NewEncoder(w).Encode(Response{
Data: data,
})
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
log.Printf("http handler failed to convert response to json %s\n", err)
}
}
func decodeJsonBody(r *http.Request, v interface{}) error {
decoder := json.NewDecoder(r.Body)
return decoder.Decode(v)
}
func updateGuestHandler(w http.ResponseWriter, r *http.Request) {
var updateGuestReq UpdateGuestRequest
err := decodeJsonBody(r, &updeateGuestReq)
if err != nil {
respondJson(w, nil, err)
return
}
data, err := UpdateGuest(&updateGuestReq)
respondJson(w, data, err)
}

Related

Passing data from handler to middleware after serving request

I have the following simple API in Go:
package main
import (
"context"
"fmt"
"net/http"
"github.com/gorilla/mux"
)
func middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Call the handler
next.ServeHTTP(w, r)
// Retrieve custom data from the request object after the request is served
customData := r.Context().Value("custom_data")
fmt.Println("Custom data:", customData)
})
}
func handler(w http.ResponseWriter, reqIn *http.Request) {
reqIn = reqIn.WithContext(context.WithValue(reqIn.Context(), "custom_data", true))
}
func main() {
r := mux.NewRouter()
// Attach the middleware to the router
r.Use(middleware)
// Attach the handler to the router
r.HandleFunc("/", handler).Methods("GET")
http.ListenAndServe(":8080", r)
}
I expected the context in the middleware to be able to access the value of "custom_data", but it is not able to, returning for that context value.
This happens even if I use Clone instead of WithContext for adding a value in the context of the request.
Looking around, specifically this post, if I instead use this as the handler:
func handler(w http.ResponseWriter, reqIn *http.Request) {
req := reqIn.WithContext(context.WithValue(reqIn.Context(), "custom_data", true))
*reqIn = *req
}
It works as expected.
But modifying the *http.Request is not the norm.
My real question that I am trying to solve is; how can I pass information from the handler to the middleware?
Adding a value to the context of the *http.Request would be able to be accessed in the middleware.
You can do the following:
func middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
custom_data := make(map[string]any)
r = r.WithContext(context.WithValue(r.Context(), "custom_data", custom_data))
// Call the handler
next.ServeHTTP(w, r)
// Retrieve custom data from the request object after the request is served
v := r.Context().Value("custom_data")
fmt.Printf("Custom data(%T): %v\n", v, v)
// or use the above defined map directly
fmt.Printf("Custom data(%T): %v\n", custom_data, custom_data)
})
}
func handler(w http.ResponseWriter, r *http.Request) {
m, ok := r.Context().Value("custom_data").(map[string]any)
if ok && m != nil {
m["value"] = true
}
}

Mock sql query Golang

I have a function :
func (db *DBSqlx) GetRefreshToken(oldToken string, tx *sqlx.Tx) (string, error) {
var refreshToken string
err := db.TemplateGet(
tx,
&refreshToken,
`query`,
oldToken,
)
if err != nil {
return "", errors.Wrap(err, "не удалось запросить рефреш токен для указанного токена")
}
return refreshToken, err
}
How to write a test for this function with mock response?
DB has type :
type DBSqlx struct {
PgConn *sqlx.DB
PgConnCtx context.Context
}
I tried to write this code. But I don't understand how to use the package correctly.
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
}
defer db.Close()
mock.ExpectQuery("query").WillReturnRows()
You can abstract your storage and underlying db handle (for making it useful with pure db and tx) using some wrapper and then substitute it with another stub interface. It does not even need to include additional libraries to your codebase.
You should keep in mind potential serialization issues with real database, NULL values and etc, adding some intergration testing with real data using https://github.com/ory/dockertest
But for simple cases wrapper is enough
// root package
type TokenStorage interface {
GetToken(ctx context.Context, oldToken string) (string, error)
}
// yourtestfile_test.go
type TokenStorageStub struct {}
func (t *TokenStorageStub) GetToken(ctx context.Context, oldToken string) (string, error) {
b := make([]byte, 16)
n, err := rand.Read(b)
if n != len(b) || err != nil {
return "", fmt.Errorf("could not successfully read from the system CSPRNG.")
}
return hex.EncodeToString(b), nil
}
// postgres || mysql || sqlite package
// TokenStorage impelements root interface using postgres.
type TokenStorage struct {
db ExtendedDB
}
func NewTokenStorage(db ExtendedDB) (*TokenStorage, error) {
if db == nil {
return nil, errors.New("provided db handle is nil")
}
return &TokenStorage{db: db}, nil
}
func (s *TokenStorage) GetToken(ctx context.Context, oldToken string) (string, error) {
const query = `SELECT ...`
var token string
if err := s.db.QueryRowContext(ctx, query, oldToken).Scan(&token); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, rootpkg.ErrTokenNotFound
}
return nil, fmt.Errorf("postgres: problem while get token using old token: %w", err)
}
return token, nil
}
// Queryer is an interface used for selection queries.
type Queryer interface {
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row
}
// Execer is an interface used for executing queries.
type Execer interface {
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
}
// ExtendedDB is a union interface which can query, and exec, with Context.
type ExtendedDB interface {
Queryer
Execer
}
You could use something like https://github.com/DATA-DOG/go-sqlmock to mock database queries with all kinds of control over responses by the database.
Though it should be noted, that mocking database queries in tests is generally not considered good testing practice because it is most likely testing the actual implementation of your program and not it's behavior.

Unwanted shutdown of selenium with goroutine

I'm developing using selenium in Golang.
I coded as below and run two clients using goroutine.
func RunSeleniumClient(port int) (selenium.WebDriver, *selenium.Service) {
caps := selenium.Capabilities{"browserName": "chrome"}
chromeCaps := chrome.Capabilities{
Path: "",
Args: []string{
"--headless",
},
}
caps.AddChrome(chromeCaps)
service, err := selenium.NewChromeDriverService("./chromedriver", port)
wd, err := selenium.NewRemote(caps, "")
if err != nil {
fmt.Println(err)
}
return wd, service
}
func DeleteCafeNotice() {
driver, service := RunSeleniumClient(4444)
defer driver.Quit()
defer service.Stop()
RunDelete(driver)
}
func DeleteCafeComment() {
driver, service := RunSeleniumClient(4448)
defer driver.Quit()
defer service.Stop()
RunDelete(driver)
}
func main() {
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
DeleteCafeNotice()
fmt.Println("End Notice")
}()
go func() {
defer wg.Done()
DeleteCafeComment()
fmt.Println("End Comment")
}()
wg.Wait()
}
I developed erasing my writings and comments.
However, if one client finishes deleting first, then the other client that you are still erasing ends together.
I've tried many things to solve this problem, but it hasn't been solved, so I leave a question.

How do I stub sqlmock to never return error?

I can not test the function NewDao that uses a database arbitrarily. I want to check whether the returned Dao have neither nil client nor nil product.
type Dao struct {
client ClientDao
product ProductDao
}
func (d *Dao) Client() ClientDao {
return d.client
}
func (d *Dao) Product() ProductDao {
return d.product
}
func NewDao(db *sql.DB) (*Dao, error) {
if db == nil {
return nil, errors.New("Can't create a dao from nil database")
}
client, err := newClientDao(db) // Uses db arbitrarily
if err != nil {
return nil, err
}
product, err := newProductDao(db) // Uses db arbitrarily
if err != nil {
return nil, err
}
return &Dao{client, product}, nil
}
I test NewDao() using sqlmock but it always fails because I don't know what the mock needs to expect.
func TestNewDao(t *testing.T) {
db, mock, err := sqlmock.New()
if err != nil {
t.Fatal("Can't create database for test dao")
}
// How to set mock to expect anything and never fail?
// mock.ExpectQuery(any) ?
dao, err := NewDao(db)
// err is never nil, because mock has no expectations
if err != nil {
t.Fatal("Can't create dao for test dao.User %q", err)
}
if dao.User() == nil {
t.Fatalf("User dao is nil")
}
if dao.Client() == nil {
t.Fatalf("Client dao is nil")
}
}
Does any one know how to stub the sqlmock for achieving my purpose? Or can appoint an alternative to sqlmock lib?
You are missing the second item in your last return in NewDao:
return &Dao{client, product}
should be:
return &Dao{client, product}, nil
Return statements have to "return" all the things declared in the function header.
Your Dao.Client and Dao.Product methods themselves make no calls to any dependency and because of that there is nothing for you to mock. There is no point in using a mock for testing these two methods.
And although testing one liners like these may not be the most sensible thing to do, if you want to test them anyway you just need to make sure that the value they return equals the value of the field that they're supposed to return.
func TestDaoClient(t *testing.T) {
var client ClientDao // TODO: prepare a client dao value for test
tests := []struct{
name string
dao *Dao
want ClientDao
}{{
name: "should return client dao",
dao: &Dao{client: client},
want: client,
}, {
name: "should return nil",
dao: &Dao{client: nil},
want: nil,
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.dao.Client(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("client got=%v, want=%v", got, tt.want)
}
})
}
}
Now you can do the same for the Dao.Product method.

Go method chaining and error handling

I want to create a method chaining API in Go. In all examples I can find the chained operations seem always to succeed which I can't guarantee. I therefore try to extend these to add the error return value.
If I do it like this
package main
import "fmt"
type Chain struct {
}
func (v *Chain)funA() (*Chain, error ) {
fmt.Println("A")
return v, nil
}
func (v *Chain)funB() (*Chain, error) {
fmt.Println("B")
return v, nil
}
func (v *Chain)funC() (*Chain, error) {
fmt.Println("C")
return v, nil
}
func main() {
fmt.Println("Hello, playground")
c := Chain{}
d, err := c.funA().funB().funC() // line 24
}
The compiler tells me chain-err-test.go:24: multiple-value c.funA() in single-value context and won't compile. Is there a good way so funcA, funcB and funcC can report an error and stop that chain?
Is there a good way so funcA, funcB and funcC can report an error and stop that chain?
Unfortunately, no, there is no good solution to your problem. Workarounds are sufficiently complex (adding in error channels, etc) that the cost exceeds the gain.
Method chaining isn't an idiom in Go (at least not for methods that can possibly error). This isn't because there is anything particularly wrong with method chains, but a consequence of the idiom of returning errors instead of panicking. The other answers are workarounds, but none are idiomatic.
Can I ask, is it not idiomatic to chain methods in Go because of the consequence of returning error as we do in Go, or is it more generally a consequence of having multiple method returns?
Good question, but it's not because Go supports multiple returns. Python supports multiple returns, and Java can too via a Tuple<T1, T2> class; method chains are common in both languages. The reason these languages can get away with it is because they idiomatically communicate errors via exceptions. Exceptions stop the method chain immediately and jump to the relevant exception handler. This is the behavior the Go developers were specifically trying to avoid by choosing to return errors instead.
You can try like that:
https://play.golang.org/p/dVn_DGWt1p_H
package main
import (
"errors"
"fmt"
)
type Chain struct {
err error
}
func (v *Chain) funA() *Chain {
if v.err != nil {
return v
}
fmt.Println("A")
return v
}
func (v *Chain) funB() *Chain {
if v.err != nil {
return v
}
v.err = errors.New("error at funB")
fmt.Println("B")
return v
}
func (v *Chain) funC() *Chain {
if v.err != nil {
return v
}
fmt.Println("C")
return v
}
func main() {
c := Chain{}
d := c.funA().funB().funC()
fmt.Println(d.err)
}
If you have control over the code and the function signature is identical you can write something like:
func ChainCall(fns ...func() (*Chain, error)) (err error) {
for _, fn := range fns {
if _, err = fn(); err != nil {
break
}
}
return
}
playground
You can make your chain lazy by collecting a slice of funtions
package main
import (
"fmt"
)
type (
chainFunc func() error
funcsChain struct {
funcs []chainFunc
}
)
func Chain() funcsChain {
return funcsChain{}
}
func (chain funcsChain) Say(s string) funcsChain {
f := func() error {
fmt.Println(s)
return nil
}
return funcsChain{append(chain.funcs, f)}
}
func (chain funcsChain) TryToSay(s string) funcsChain {
f := func() error {
return fmt.Errorf("don't speek golish")
}
return funcsChain{append(chain.funcs, f)}
}
func (chain funcsChain) Execute() (i int, err error) {
for i, f := range chain.funcs {
if err := f(); err != nil {
return i, err
}
}
return -1, nil
}
func main() {
i, err := Chain().
Say("Hello, playground").
TryToSay("go cannot into chains").
Execute()
fmt.Printf("i: %d, err: %s", i, err)
}
You don't actually need channels and/or contexts to get something like this to work. I think this implementation meets all your requirements but needless to say, this leaves a sour taste. Go is not a functional language and it's best not to treat it as such.
package main
import (
"errors"
"fmt"
"strconv"
)
type Res[T any] struct {
Val T
Halt bool
Err error
}
// executes arguments until a halting signal is detected
func (r *Res[T]) Chain(args ...func() *Res[T]) *Res[T] {
temp := r
for _, f := range args {
if temp = f(); temp.Halt {
break
}
}
return temp
}
// example function, converts any type -> string -> int -> string
func (r *Res[T]) funA() *Res[string] {
s := fmt.Sprint(r.Val)
i, err := strconv.Atoi(s)
if err != nil {
r.Err = fmt.Errorf("wrapping error: %w", err)
}
fmt.Println("the function down the pipe is forced to work with Res[string]")
return &Res[string]{Val: strconv.Itoa(i), Err: r.Err}
}
func (r *Res[T]) funB() *Res[T] {
prev := errors.Unwrap(r.Err)
fmt.Printf("unwrapped error: %v\n", prev)
// signal a halt if something is wrong
if prev != nil {
r.Halt = true
}
return r
}
func (r *Res[T]) funC() *Res[T] {
fmt.Println("this one never gets executed...")
return r
}
func (r *Res[T]) funD() *Res[T] {
fmt.Println("...but this one does")
return r
}
func funE() *Res[string] {
fmt.Println("Chain can even take non-methods, but beware of nil returns")
return nil
}
func main() {
r := Res[string]{}
r.Chain(r.funA, r.funB, r.funC).funD().Chain(funE).funC() // ... and so on
}
How about this approach: Create a struct that delegates Chain and error, and return it instead of two values. e.g.:
package main
import "fmt"
type Chain struct {
}
type ChainAndError struct {
*Chain
error
}
func (v *Chain)funA() ChainAndError {
fmt.Println("A")
return ChainAndError{v, nil}
}
func (v *Chain)funB() ChainAndError {
fmt.Println("B")
return ChainAndError{v, nil}
}
func (v *Chain)funC() ChainAndError {
fmt.Println("C")
return ChainAndError{v, nil}
}
func main() {
fmt.Println("Hello, playground")
c := Chain{}
result := c.funA().funB().funC() // line 24
fmt.Println(result.error)
}