High-performance radix-tree routing with SoA hash table acceleration, root absorption, named parameters, wildcards, route groups, and pre-computed middleware chains. Static lookups in 7–12 ns, parametric matches in 19–36 ns, zero allocations.
Register handlers by HTTP method. Each handler receives a *Request and
*Response:
s := core.New() s.Router.GET("/hello", func(req *core.Request, resp *core.Response) { resp.String("Hello, World!") }) s.Router.POST("/users", func(req *core.Request, resp *core.Response) { resp.Status(201).String("Created") })
The handler signature is always:
type HandlerFunc func(req *Request, resp *Response)
ALOS supports all 9 standard HTTP methods plus a catch-all:
| Method | Function | Description |
|---|---|---|
| GET | Router.GET(path, handler) |
Read resources |
| POST | Router.POST(path, handler) |
Create resources |
| PUT | Router.PUT(path, handler) |
Replace resources |
| DELETE | Router.DELETE(path, handler) |
Delete resources |
| PATCH | Router.PATCH(path, handler) |
Partial updates |
| HEAD | Router.HEAD(path, handler) |
Headers only |
| OPTIONS | Router.OPTIONS(path, handler) |
CORS preflight |
| CONNECT | Router.Handle("CONNECT", ...) |
Tunnel |
| TRACE | Router.Handle("TRACE", ...) |
Diagnostic |
| ANY | Router.ANY(path, handler) |
All methods |
s.Router.ANY("/health", func(req *core.Request, resp *core.Response) { resp.JSON([]byte(`{"status":"ok"}`)) }) s.Router.Handle("CONNECT", "/proxy", connectHandler)
Use :name to capture named segments from the URL. Access them with
req.ParamValue():
s.Router.GET("/users/:id", func(req *core.Request, resp *core.Response) { id := req.ParamValue("id") resp.String("User: " + id) }) s.Router.GET("/posts/:postId/comments/:commentId", func(req *core.Request, resp *core.Response) { postId := req.ParamValue("postId") commentId := req.ParamValue("commentId") resp.String(postId + "/" + commentId) })
| Pattern | URL | Params |
|---|---|---|
/users/:id |
/users/42 |
id = "42" |
/posts/:postId/comments/:commentId |
/posts/5/comments/99 |
postId = "5", commentId = "99" |
Up to 8 parameters per route are supported with zero-allocation lookup. Parameters are stored in a fixed-size array on the request.
Use *name to capture everything after a prefix. This is useful for file serving
or catch-all routes:
s.Router.GET("/files/*filepath", func(req *core.Request, resp *core.Response) { path := req.ParamValue("filepath") resp.SendFile("./static/" + path) }) s.Router.GET("/*any", func(req *core.Request, resp *core.Response) { resp.Status(404).String("Not found") })
Runtime note: wildcard matching works on the native Linux
io_uring runtime, but this SendFile example uses the
streamed-response compatibility path, which is not yet available on the native worker
backend.
| Pattern | URL | Params |
|---|---|---|
/files/*filepath |
/files/css/style.css |
filepath = "css/style.css" |
/*any |
/some/deep/path |
any = "some/deep/path" |
A wildcard must be the last segment in the pattern. It captures everything after the prefix including slashes.
Group routes under a common prefix with shared middleware. Groups can be nested:
api := s.Router.Group("/api", core.Logger()) api.GET("/users", listUsers) api.POST("/users", createUser) api.GET("/users/:id", getUser) admin := api.Group("/admin", authMiddleware) admin.GET("/stats", getStats) admin.DELETE("/users/:id", deleteUser)
Middleware applied to a group is cumulative — nested groups inherit parent middleware plus their own.
s.Router.Use(core.Recovery()) s.Router.Use(core.Logger()) s.Router.Use(s.CORS.Middleware()) protected := s.Router.Group("/dashboard", authMiddleware)
Groups support Use() to add middleware after creation, and ANY()
for catch-all method registration:
api := s.Router.Group("/api") api.Use(core.RealIP(), core.RequestID()) api.ANY("/health", healthCheck) api.GET("/users", listUsers) api.POST("/users", createUser)
| RouteGroup Method | Description |
|---|---|
Group(prefix, middleware...) |
Create a nested sub-group |
Use(middleware...) |
Add middleware to the group |
GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS |
Register handler for specific method |
ANY(path, handler) |
Register handler for all methods |
After registering all routes and middleware, call Build() to pre-compute the
middleware chains:
s.Router.GET("/", homeHandler) s.Router.GET("/about", aboutHandler) s.Router.Build() log.Fatal(s.ListenAndServeTLS())
Why Build()? Build pre-computes middleware chains, absorbs the empty root node into its first child, and constructs a struct-of-arrays hash table for O(1) static route lookups. This means zero overhead per-request — routing resolves in 7–12 ns for statics, 19–36 ns for parametric paths, with zero allocations.
Customize the response for unmatched routes or disallowed methods:
s.Router.NotFound(func(req *core.Request, resp *core.Response) { resp.Status(404).JSON([]byte(`{"error":"route not found"}`)) }) s.Router.MethodNotAllowed(func(req *core.Request, resp *core.Response) { resp.Status(405).JSON([]byte(`{"error":"method not allowed"}`)) })
Programmatically check if a route exists or look up its handler:
exists := s.Router.Match("GET", "/users/42") handler := s.Router.Lookup("GET", "/users/42", req) if handler != nil { handler(req, resp) }
Register middleware that runs on every route using Use(). Accepts variadic
middleware:
s.Router.Use(core.Recovery(), core.Logger(), core.SecurityHeaders())
Middleware executes in registration order. Each wraps the next:
s.Router.Use(core.Recovery()) s.Router.Use(core.Logger()) s.Router.Use(core.Compress())
Groups inherit global middleware and can add their own:
s.Router.Use(core.Recovery()) api := s.Router.Group("/api", core.Logger()) api.Use(core.RealIP()) api.GET("/data", func(req *core.Request, resp *core.Response) { resp.String("Recovery + Logger + RealIP + handler") })
Signature:
func (r *Router) Use(middleware ...MiddlewareFunc)
MiddlewareFunc is func(HandlerFunc) HandlerFunc
Apply middleware to a single handler without registering it globally. Useful for per-route middleware:
s.Router.GET("/admin/dashboard", core.Chain( dashboardHandler, authMiddleware, core.Timeout(5 * time.Second), ))
Mix global and per-route middleware:
s.Router.Use(core.Recovery(), core.Logger()) s.Router.GET("/public", publicHandler) s.Router.POST("/upload", core.Chain( uploadHandler, core.BodyLimit(10 * 1024 * 1024), core.Timeout(30 * time.Second), ))
Signature:
func Chain(handler HandlerFunc, middleware ...MiddlewareFunc) HandlerFunc
A complete routing setup with groups, middleware, custom error handlers, and route lookup:
s := core.New() s.Router.Use(core.Recovery(), core.Logger(), core.Compress()) s.Router.GET("/", func(req *core.Request, resp *core.Response) { resp.String("Welcome") }) s.Router.GET("/health", func(req *core.Request, resp *core.Response) { resp.JSON([]byte(`{"status":"ok"}`)) }) api := s.Router.Group("/api/v1", core.RealIP()) api.GET("/users", listUsers) api.GET("/users/:id", getUser) api.POST("/users", core.Chain(createUser, core.BodyLimit(1024*1024))) api.DELETE("/users/:id", deleteUser) admin := api.Group("/admin", authMiddleware) admin.GET("/stats", statsHandler) admin.POST("/purge", purgeHandler) s.Router.GET("/static/*filepath", func(req *core.Request, resp *core.Response) { resp.SendFile("./public/" + req.ParamValue("filepath")) }) s.Router.NotFound(func(req *core.Request, resp *core.Response) { resp.Status(404).JSON([]byte(`{"error":"not found"}`)) }) s.Router.MethodNotAllowed(func(req *core.Request, resp *core.Response) { resp.Status(405).JSON([]byte(`{"error":"method not allowed"}`)) }) s.Router.Build() log.Fatal(s.ListenAndServeTLS())
Runtime note: the router, groups, and middleware chain all run on the
native Linux io_uring runtime. The
/static/*filepath example still uses SendFile, which remains
a compatibility-only path for now.