A generic web extension to net/http
Optic helps you call backend functions from your frontend by sending a regular go struct and recieving a struct back
It is especially useful when making requests to a go service from a go client (WASM app, cli, tui ...)
go get github.com/nanvenomous/optic
Then import with
import (
"github.com/nanvenomous/optic"
)
Define the entities and error interface
type solution struct {
Answer int
}
type division struct {
Top int
Bottom int
}
type userHTTPError struct {
Message string
Code int
}
func (e *userHTTPError) GetCode() int {
return e.Code
}
Setup the service route
func divide(recieved *division, _ *http.Request) (*solution, optic.HTTPError) {
if recieved.Bottom == 0 { // return an error
return nil, &userHTTPError{Code: http.StatusUnprocessableEntity, Message: "Impossible to divide by Zero"}
}
return &solution{Answer: recieved.Top / recieved.Bottom}, nil
}
func main() {
var (
err error
encodeErr = &userHTTPError{Code: http.StatusInternalServerError, Message: "Failed to encode your response."}
decodeErr = &userHTTPError{Code: http.StatusNotAcceptable, Message: "Failed to decode your request body."}
mux *http.ServeMux
)
mux = http.NewServeMux()
optic.SetupService(port, userOpticRoute, encodeErr, decodeErr, mux)
// An optical mirror simply recieves information and sends information back
optic.Mirror(divide) // by default optic will use function name as route
err = optic.Serve() // run the service
}
Setup the client and make a request
func main {
optic.SetupClient(host, port, userOpticRoute, false)
// Make requests
var (
err error // internal error
httpErr *userHTTPError // service exception
sln solution // output
)
// send receive
httpErr, err = optic.Glance[userHTTPError]("/divide/", &division{Top: 4, Bottom: 2}, &sln)
fmt.Println(err, httpErr) // <nil> <nil>
fmt.Println(sln.Answer) // 2
}
Optic is drop in compatible with net/http
Give optic a *http.ServerMux
& a special route where it will handle all you functions
Then do whatever else you want with that mux
func main() {
var (
err error
mux *http.ServeMux
)
mux = http.NewServeMux()
optic.SetupService(port, userOpticRoute, encodeErr, decodeErr, mux)
// Add other routes not handled by optic, as you would with any net/http service
mux.HandleFunc("/health-check/", func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
})
// optic can register middleware for you
optic.RegisterMiddleware(exampleMiddleware)
// or you can do it yourself
var (
handler http.Handler
)
handler = exampleMiddleware(mux)
}
func exampleMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// add some middleware (like CORS for example)
w.Header().Set("Access-Control-Allow-Origin", "*")
next.ServeHTTP(w, r)
})
}
For the full example in code see ./examples/main.go
I am planning some outreach so I can get feedback from other go developers & aiming to address major concerns between each post
- reddit post
- Lack of transparency in error handling, poor naming convention
Exception
, removing unessecary generics resolution commit - using revive for better code analysis, tightened up module exports - resolution commit
- Lack of transparency in error handling, poor naming convention
Lines 1 to 3 in b8a94eb
I drew some inspiration from leptos server functions