RouterStore is a lightweight high performance HTTP request router and data store for Go. It registers requests patterns with any kind of data and retrieves the data with matching request paths.
Typical routers (often called multiplexers or muxes) are focused on providing a high performance alternative to Go's stardard HTTP library in the net/http
package. If that is your need, there are several great libraries to choose from. This library is specifically used for resolving application data for matching HTTP request paths. For example, the data plane of a service mesh would need to find the service information for each incoming request in order to proxy the network traffic.
This router's algorithm uses the similar principles as others to achieve high performance, small memory footprint, and scalability. A radix tree structure and zero memory allocation implementation provides performance comparable to the very fast httprouter library. In addition, this router implements desirable features for service meshes and API gateways.
Prioritized matching: Other routers can return unexpected results when a request path can match multiple routes. For example, the router might return the first matching route when a more specific different route is a better match. This router determines the best match by applying the following priority order:
- Static routes
- Regular expression variable routes (in order of registration)
- Simple variable routes
- Wildcard routes (when no child routes match)
Non-conflicting path variables: Sometimes, an API might need to name a path variable differently. E.g.:
GET /people/:id
=> Return the person by their identifierGET /people/:id/tasks
=> Returns the person's tasksGET /people/:last/:first
=> Find the person by their first and last names
The router's match result provides the correct variable name and values for the provided request path. E.g.:
GET /people/1234/tasks
Returns id = 1234
GET /people/doe/john
Returns first = john, last = doe
Great performance: This is attributed to the radix tree structure for building routes and that the matching logic allocates 0 bytes of heap. Not even path parameters create garbage.
Bad routes return errors: This is a small but important detail. Other routers will panic when the developer tried to add a bad route. This makes sense for an API, but not for a proxy that needs to handle bad configuration.
package main
import (
"log"
"github.com/prizem-io/routerstore"
)
type Service struct {
Name string
Version string
}
func main() {
helloService := Service{
Name: "Hello",
Version: "V1",
}
router := routerstore.New()
router.GET("/people", &helloService)
router.GET("/people/:id", &helloService)
router.GET("/people/:id/tasks", &helloService)
router.GET("/people/:last/:first", &helloService)
var result routerstore.Result
var err error
var service *Service
log.Printf("GET /people/1234")
err = router.Match(routerstore.GET, "/people/1234", &result)
service = checkResult(&result, err)
log.Printf("\tService %s, Version %s", service.Name, service.Version)
log.Printf("\tPerson ID: %s", result.Param("id"))
log.Printf("GET /people/doe/john")
err = router.Match(routerstore.GET, "/people/doe/john", &result)
service = checkResult(&result, err)
log.Printf("\tService %s, Version %s", service.Name, service.Version)
log.Printf("\tPerson Name: %s %s", result.Param("first"), result.Param("last"))
}
func checkResult(result *routerstore.Result, err error) *Service {
if err != nil {
log.Fatalf("could not match: %v", err)
}
service, ok := result.Data.(*Service)
if !ok {
log.Fatal("Unexpected result data")
}
return service
}