How to nest routers in Go julienschmidt/httprouter?

610 views Asked by At

I want to expose the following URLs from my service:

GET /api/foo
GET /api/bar

I also want to structure it as a router nested inside another. The toplevel router will match all requests to /api and serve them with the nested router which will match requests to /foo and /bar. Basically, namespacing.

I could just have a single router and give the /api prefix to both the routes:

router.GET("/api/foo", apiFoo)
router.GET("/api/bar", apiBar)

But I would like the convenience of having a single router for all routes inside the /api prefix so that I can add appropriate middlewares to them all with a single function call.

Here's what I tried:

package main

import (
    "log"
    "net/http"

    "github.com/julienschmidt/httprouter"
)

func apiFoo(w http.ResponseWriter, r *http.Request) {}
func apiBar(w http.ResponseWriter, r *http.Request) {}

func main() {
    api := httprouter.New()
    api.HandlerFunc(http.MethodGet, "/foo", apiFoo)
    api.HandlerFunc(http.MethodGet, "/bar", apiBar)

    router := httprouter.New()
    router.Handler(http.MethodGet, "/api", api)

    log.Fatal(http.ListenAndServe(":8080", router))
}

However, getting 404 not found on going to http://localhost:8080/api/foo or http://localhost:8080/api/bar

I had thought that nested routers would work because routers implement the http.Handler interface. What am I missing?

2

There are 2 answers

0
The Fool On BEST ANSWER

httprouter does not concatenate the path, so you can't do it that way. Both routers will just inspect the request path and act accordingly.

There is an open pull request for 7 years, that would implement it. You could have a look there and implement similar logic yourself, the PR is based on concatenating the path. Maybe you can write a small helper function for that.

If you are willing to switch the router package, you could look into alternatives such as chi, which support router groups.

0
tpjg On

or use StripPrefix from the standard library

http.ListenAndServe(":8080", http.StripPrefix("/api", api))