# Advanced Routing with Go 1.22

Before Go 1.22, if you wanted advanced routing features like method routing, path parameters, etc., you wrote a lot of boilerplate code or reached out to an external library like Gorilla Mux or another web framework.

Starting from Go 1.22, the standard library's `net/http` package introduced many advanced routing features; in this post, we'll take a look at a few of them.

> To make use of these features, make sure that the version in your `go.mod` file is 1.22.X

## Path Parameters

```go
package main

import (
	"fmt"
	"net/http"
)

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/product/{id}", func(w http.ResponseWriter, r *http.Request) {
		productId := r.PathValue("id")
		fmt.Fprintf(w, "displaying properties for product %s", productId)
	})
	fmt.Println("Server is listening on port 8000")
	http.ListenAndServe(":8000", mux)
}
```

In the above example, we define a route with a path parameter, `"/product/{id}"`

We have to wrap the parameter around `{}` . To get the parameter value in our handler, we can use the `PathValue` method in the request object.

## Method Routing

We can also have separate handlers for each HTTP method.

```go
mux := http.NewServeMux()
mux.HandleFunc("GET /product/{id}", func(w http.ResponseWriter, r *http.Request) {
	productId := r.PathValue("id")
	fmt.Fprintf(w, "displaying properties for product %s", productId)
})

mux.HandleFunc("POST /product/{id}", func(w http.ResponseWriter, r *http.Request) {
	productId := r.PathValue("id")
	fmt.Fprintf(w, "creating product with id %s", productId)
})
```

Now, our route pattern has been expanded to include an HTTP verb

"\[METHOD \]\[PATH\]"

> There should exactly a single space between the method and the path.

## Sub-routing

Sub-routing allows us to group common routes. For example, you can group "customer" and "admin" routes under different prefixes, `/customer` and `/admin` respectively.

We can also use this to version our API, eg `/v1`, `/v2`

```go
package main

import (
	"fmt"
	"net/http"
)

func main() {
	customerRouter := http.NewServeMux() // 3
	adminRouter := http.NewServeMux() // 2

	// customer handlers
	customerRouter.HandleFunc("/sign-in", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Customer Sign in")
	})
	// admin handlers
	adminRouter.HandleFunc("/sign-in", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Admin Sign in")
	})

	router := http.NewServeMux() // 1 - root router
	router.Handle("/customer/", http.StripPrefix("/customer", customerRouter))
	router.Handle("/admin/", http.StripPrefix("/admin", adminRouter))

	fmt.Println("Server is running on port 8000")
	http.ListenAndServe(":8000", router)
}
```

If you have worked with a framework like [`Express JS`](https://expressjs.com/), this should look familiar.

We create three routers with `http.NewServeMux()` .

Notice that we first strip out the path group prefix in our root router. This is necessary, or else none of the routes in our sub-routers will be matched against.

Also, our route prefixes must end in a `"/"`

## Middlewares

Middleware is code that independently acts on a request before or after your normal application handlers.

In Go, the building block of HTTP handling is the `Handler` interface.

```go
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}
```

`http.ServeMux` and `http.Handler` (the second parameter to `http.HandleFunc`) implement this interface.

With this in mind, let's create a `Logger` middleware.

```go
func Logger(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		next.ServeHTTP(w, r)
		log.Println(time.Since(start), r.Method, r.URL.Path)
	})
}
```

Middlewares generally have the following signature:

```go
func Middleware(next http.Handler) http.Handler
```

They take in an HTTP handler and return an HTTP handler.

In the `Logger` middleware, on line 4, we call the next handler's `ServeHTTP` method.

For the Express JS devs, this is akin to calling the `next` function in an express middleware.

Now, how do we use this middleware?

```go
http.ListenAndServe(":8000", middleware.Logger(router)) // 1
// OR
router.Handle("/admin/", http.StripPrefix("/admin", middleware.Logger(adminRouter))) // 2
// OR
router.Handle("/customer/", http.StripPrefix("/customer", middleware.Logger(customerRouter))) // 3
```

With this, we can enable logging for all routes or a sub-route.

You can access all the code examples [here](https://github.com/The-Flash/go122-intro)
