Skip to main content
Version: Next

🎣 Hooks

With hooks, you can manipulate the client on before request/after response stages or more complex logging/tracing cases.

There are 2 kinds of hooks:

Request Hooks

They are called before the HTTP request has been sent. You can use them make changes on Request object.

You need to use RequestHook func(*Client, *Request) error function signature while creating the hooks. You can use request hooks to change host URL, log request properties etc. Here is an example about how to create request hooks:

type Repository struct {
Name string `json:"name"`
FullName string `json:"full_name"`
Description string `json:"description"`
Homepage string `json:"homepage"`

Owner struct {
Login string `json:"login"`
} `json:"owner"`
}

func main() {
cc := client.New()

cc.AddRequestHook(func(c *client.Client, r *client.Request) error {
r.SetURL("https://api.github.com/" + r.URL())

return nil
})

resp, err := cc.Get("repos/gofiber/fiber")
if err != nil {
panic(err)
}

var repo Repository
if err := resp.JSON(&repo); err != nil {
panic(err)
}

fmt.Printf("Status code: %d\n", resp.StatusCode())

fmt.Printf("Repository: %s\n", repo.FullName)
fmt.Printf("Description: %s\n", repo.Description)
fmt.Printf("Homepage: %s\n", repo.Homepage)
fmt.Printf("Owner: %s\n", repo.Owner.Login)
fmt.Printf("Name: %s\n", repo.Name)
fmt.Printf("Full Name: %s\n", repo.FullName)
}
Click here to see the result
Status code: 200
Repository: gofiber/fiber
Description: ⚡️ Express inspired web framework written in Go
Homepage: https://gofiber.io
Owner: gofiber
Name: fiber
Full Name: gofiber/fiber

There are also some builtin request hooks provide some functionalities for Fiber client. Here is a list of them:

  • parserRequestURL: parserRequestURL customizes the URL according to the path params and query params. It's necessary for PathParam and QueryParam methods.

  • parserRequestHeader: parserRequestHeader sets request headers, cookies, body type, referer, user agent according to client and request proeprties. It's necessary to make request header and cookiejar methods functional.

  • parserRequestBody: parserRequestBody serializes the body automatically. It is useful for XML, JSON, form, file bodies.

info

If any error returns from request hook execution, it will interrupt the request and return the error.

func main() {
cc := client.New()

cc.AddRequestHook(func(c *client.Client, r *client.Request) error {
fmt.Println("Hook 1")
return errors.New("error")
})

cc.AddRequestHook(func(c *client.Client, r *client.Request) error {
fmt.Println("Hook 2")
return nil
})

_, err := cc.Get("https://example.com/")
if err != nil {
panic(err)
}
}
Click here to see the result
Hook 1.
panic: error

goroutine 1 [running]:
main.main()
main.go:25 +0xaa
exit status 2

Response Hooks

They are called after the HTTP response has been completed. You can use them to get some information about response and request.

You need to use ResponseHook func(*Client, *Response, *Request) error function signature while creating the hooks. You can use response hook for logging, tracing etc. Here is an example about how to create response hooks:

func main() {
cc := client.New()

cc.AddResponseHook(func(c *client.Client, resp *client.Response, req *client.Request) error {
fmt.Printf("Response Status Code: %d\n", resp.StatusCode())
fmt.Printf("HTTP protocol: %s\n\n", resp.Protocol())

fmt.Println("Response Headers:")
resp.RawResponse.Header.VisitAll(func(key, value []byte) {
fmt.Printf("%s: %s\n", key, value)
})

return nil
})

_, err := cc.Get("https://example.com/")
if err != nil {
panic(err)
}
}
Click here to see the result
Response Status Code: 200
HTTP protocol: HTTP/1.1

Response Headers:
Content-Length: 1256
Content-Type: text/html; charset=UTF-8
Server: ECAcc (dcd/7D5A)
Age: 216114
Cache-Control: max-age=604800
Date: Fri, 10 May 2024 10:49:10 GMT
Etag: "3147526947+gzip+ident"
Expires: Fri, 17 May 2024 10:49:10 GMT
Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
Vary: Accept-Encoding
X-Cache: HIT

There are also some builtin request hooks provide some functionalities for Fiber client. Here is a list of them:

  • parserResponseCookie: parserResponseCookie parses cookies and saves into the response objects and cookiejar if it's exists.

  • logger: logger prints some RawRequest and RawResponse information. It uses log.CommonLogger interface for logging.

info

If any error is returned from executing the response hook, it will return the error without executing other response hooks.

func main() {
cc := client.New()

cc.AddResponseHook(func(c *client.Client, r1 *client.Response, r2 *client.Request) error {
fmt.Println("Hook 1")
return nil
})

cc.AddResponseHook(func(c *client.Client, r1 *client.Response, r2 *client.Request) error {
fmt.Println("Hook 2")
return errors.New("error")
})

cc.AddResponseHook(func(c *client.Client, r1 *client.Response, r2 *client.Request) error {
fmt.Println("Hook 3")
return nil
})

_, err := cc.Get("https://example.com/")
if err != nil {
panic(err)
}
}
Click here to see the result
Hook 1
Hook 2
panic: error

goroutine 1 [running]:
main.main()
main.go:30 +0xd6
exit status 2
info

Hooks work as FIFO (first-in-first-out). You need to check the order while adding the hooks.

func main() {
cc := client.New()

cc.AddRequestHook(func(c *client.Client, r *client.Request) error {
fmt.Println("Hook 1")
return nil
})

cc.AddRequestHook(func(c *client.Client, r *client.Request) error {
fmt.Println("Hook 2")
return nil
})

_, err := cc.Get("https://example.com/")
if err != nil {
panic(err)
}
}
Click here to see the result
Hook 1
Hook 2