A Go package implementing the RES-Service protocol for Resgate - Real-time API Gateway.
When you want to create stateless REST API services but need to have all your resources updated in real time on your reactive web clients.
All resources and methods served by RES services are made accessible through Resgate through both:
- Ordinary HTTP REST requests
- WebSocket using ResClient
With ResClient, all resources will be updated in real time, without having to write a single line of client code to handle specific events. It just works.
go get github.com/jirenius/go-res
- Hello World - Single model updated in real time
- Book Collection - List of books, added, edited, and updated in real time
package main
import res "github.com/jirenius/go-res"
func main() {
s := res.NewService("myservice")
s.Handle("mymodel",
res.Access(res.AccessGranted),
res.GetModel(func(r res.ModelRequest) {
r.Model(map[string]string{"greeting": "welcome"})
}),
)
s.ListenAndServe("nats://localhost:4222")
}
While a RES service communicates over a message broker (NATS Server), instead of listening to HTTP request, the pattern of requests and responses are similar.
s := res.NewService("myservice")
mymodel := map[string]interface{}{"name": "foo", "value": 42}
s.Handle("mymodel",
res.Access(res.AccessGranted),
res.GetModel(func(r res.ModelRequest) {
r.Model(mymodel)
}),
)
mycollection := []string{"first", "second", "third"}
s.Handle("mycollection",
res.Access(res.AccessGranted),
res.GetCollection(func(r res.CollectionRequest) {
r.Collection(mycollection)
}),
)
s.Handle("article.$id",
res.Access(res.AccessGranted),
res.GetModel(func(r res.ModelRequest) {
article := getArticle(r.PathParam("id"))
if article == nil {
r.NotFound()
} else {
r.Model(article)
}
}),
)
s.Handle("math",
res.Access(res.AccessGranted),
res.Call("double", func(r res.CallRequest) {
var p struct {
Value int `json:"value"`
}
r.ParseParams(&p)
r.OK(p.Value * 2)
}),
)
A change event will update the model on all subscribing clients.
s.With("myservice.mymodel", func(r res.Resource) {
mymodel["name"] = "bar"
r.ChangeEvent(map[string]interface{}{"name": "bar"})
})
An add event will update the collection on all subscribing clients.
s.With("myservice.mycollection", func(r res.Resource) {
mycollection = append(mycollection, "fourth")
r.AddEvent("fourth", len(mycollection)-1)
})
s.Handle("myauth",
res.Auth("login", func(r res.AuthRequest) {
var p struct {
Password string `json:"password"`
}
r.ParseParams(&p)
if p.Password != "mysecret" {
r.InvalidParams("Wrong password")
} else {
r.TokenEvent(map[string]string{"user": "admin"})
r.OK(nil)
}
}),
)
s.Handle("mymodel",
res.Access(func(r res.AccessRequest) {
var t struct {
User string `json:"user"`
}
r.ParseToken(&t)
if t.User == "admin" {
r.AccessGranted()
} else {
r.AccessDenied()
}
}),
res.GetModel(func(r res.ModelRequest) {
r.Model(mymodel)
}),
)
s.ListenAndServe("nats://localhost:4222")
Inspiration on the go-res API has been taken from github.com/go-chi/chi, a great package when writing ordinary HTTP services, and will continue to do so when it is time to implement Middleware, sub-handlers, and mounting.
The go-res package is still under development, and commits may still contain breaking changes. It should only be used for educational purpose. Any feedback on the package API or its implementation is highly appreciated!
If you find any issues, feel free to report them as an Issue.