Skip to main content

🔀 Middlewares

đŸ’Ē Priority​

It's essential to understand how middlewares work. The middleware that gets called the last, has the most "power", since it can override values that have been set in previous middlewares.

If you call .Use() anywhere, you will register a global middleware for the entire router. On the other hand, .With() only defines a middleware for one single endpoint, being the most recent one defined.

Have a look at this example to build an understanding of the execution order.

package main

import (
"fmt"
"github.com/Gebes/there/v2"
"github.com/Gebes/there/v2/status"
"log"
)

type User struct {
Id int `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Admin bool `json:"admin,omitempty"`
}

func main() {
router := there.NewRouter()

router.Use(func(request there.Request, next there.Response) there.Response {
fmt.Println("Global 1")
return there.Headers(map[string]string{"Test": "Global 1"}, next)
})

router.Use(func(request there.Request, next there.Response) there.Response {
fmt.Println("Global 2")
return there.Headers(map[string]string{"Test": "Global 2"}, next)
})

router.Get("/", Get).With(func(request there.Request, next there.Response) there.Response {
fmt.Println("Specific 1")
return there.Headers(map[string]string{"Test": "Specific 1"}, next)
}).With(func(request there.Request, next there.Response) there.Response {
fmt.Println("Specific 2")
return there.Headers(map[string]string{"Test": "Specific 2"}, next)
})

err := router.Listen(8080)
if err != nil {
log.Fatalln("Could not listen to 8080", err)
}
}

func Get(request there.Request) there.Response {
return there.Auto(status.OK, User{
Id: 5,
Name: "Chris",
Admin: true,
})
}

Output

Specific 2
Specific 1
Global 2
Global 1

and the Test-Header gets set to Global 1.

đŸžī¸ Default middlewares​

There provides several useful middlewares out of the box.

đŸĨ Recoverer​

Recovers from a panic, should be the first global middleware if used

router.Use(middlewares.Recoverer)

đŸŠē Logger​

Logs every request and the body of messages with an error Screenshot The order of the middlewares matters. If you have a Gzip Middleware, you should add the Logger Middleware afterwards. Otherwise, the Logger Middleware can only read the gibberish compressed data. Screenshot of gibberish data

âŦ‡ī¸ Gzip​

Compressing data can be useful, to improve the response time of the API, since less networking is required.

This custom middleware simply compresses every result.

However, be careful again! If you have a generic endpoint in your project, and you also use the there.Gzip() response as a wrapper, you will compress your data twice.

router.Use(func(request there.Request, next there.Response) there.Response {
return there.Gzip(next)
})

👮‍ CORS​

Be careful with the CORS headers you set. If you are building an open API, you may want to expose every route for every website. However, if you use authentication, you should secure your endpoint in a better way.

router.Use(middlewares.Cors(middlewares.CorsAllowAllConfiguration()))
package main

import (
"errors"
"github.com/Gebes/there/v2"
"github.com/Gebes/there/v2/middlewares"
"github.com/Gebes/there/v2/status"
"log"
"math/rand"
)

type User struct {
Id int `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Admin bool `json:"admin,omitempty"`
}

func main() {
router := there.NewRouter()

router.Use(middlewares.Recoverer)
router.Use(middlewares.Logger())
router.Use(func(request there.Request, next there.Response) there.Response {
return there.Gzip(next)
})
router.Use(middlewares.Cors(middlewares.CorsAllowAllConfiguration()))

router.Get("/", Get)

err := router.Listen(8080)
if err != nil {
log.Fatalln("Could not listen to 8080", err)
}
}

func Get(request there.Request) there.Response {
if rand.Int()%3 == 0 {
return there.Error(status.InternalServerError, errors.New("something went wrong"))
}
return there.Auto(status.OK, User{
Id: 5,
Name: "Chris",
Admin: true,
})
}