🎣 Hooks
Hooks let you intercept and modify the request or response flow of the Fiber client. They are useful for:
- Changing request parameters (e.g., URL, headers) before sending the request.
- Logging request and response details.
- Integrating complex tracing or monitoring tools.
- Handling authentication, retries, or other custom logic.
There are two kinds of hooks:
Request Hooks
Request hooks are functions executed before the HTTP request is sent. They follow the signature:
type RequestHook func(*Client, *Request) error
A request hook receives both the Client
and the Request
objects, allowing you to modify the request before it leaves your application. For example, you could:
- Change the host URL.
- Log request details (method, URL, headers).
- Add or modify headers or query parameters.
- Intercept and apply custom authentication logic.
Example:
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()
// Add a request hook that modifies the request URL before sending.
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
Built-in Request Hooks
Fiber includes built-in request hooks:
- parserRequestURL: Normalizes and customizes the URL based on path and query parameters. Required for
PathParam
andQueryParam
methods. - parserRequestHeader: Sets request headers, cookies, content type, referer, and user agent based on client and request properties.
- parserRequestBody: Automatically serializes the request body (JSON, XML, form, file uploads, etc.).
If a request hook returns an error, Fiber stops the request and returns the error immediately.
Example with Multiple Hooks:
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
Response hooks are functions executed after the HTTP response is received. They follow the signature:
type ResponseHook func(*Client, *Response, *Request) error
A response hook receives the Client
, Response
, and Request
objects, allowing you to inspect and modify the response or perform additional actions such as logging, tracing, or processing response data.
Example:
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:")
for key, value := range resp.RawResponse.Header.All() {
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
Built-in Response Hooks
Fiber includes built-in response hooks:
- parserResponseCookie: Parses cookies from the response and stores them in the response object and cookie jar if available.
- logger: Logs information about the raw request and response. It uses the
log.CommonLogger
interface.
If a response hook returns an error, Fiber skips the remaining hooks and returns that error.
Example with Multiple 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
Hook Execution Order
Hooks run in FIFO order (first in, first out), so they're executed in the order you add them. Keep this in mind when adding multiple hooks, as the order can affect the outcome.
Example:
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