Local Values In Fiber

For much of my use of Fiber, whenever I needed to tack on a value that’s scoped to the request, I used the standard approach in Go whenever a Context is available: define a new key and call context.WithValue()

type userKeyType struct{}

var userKey = userKeyType{}

func setUser(c *fiber.Ctx) error { usr, err := fetchUserFromSession() if err != nil { return err }

newCtx := context.WithValue(c.UserContext(), userKey, usr)
c.SetUserContext(newCtx)
<span class="hljs-keyword">return</span> c.Next()

}

func currentUser(c *fiber.Ctx) models.User { usr := c.UserContext().Value(userKey).(models.User) return usr }

This works, and is useful if you’re calling service methods that need the user in the context for some reasons. But I recently learnt that Fiber has an alternative way to set request-scoped values. This is done using Locals:

func setUser(c *fiber.Ctx) error {
    usr, err := fetchUserFromSession()
    if err != nil {
        return err
    }
c.Locals(<span class="hljs-string">"user"</span>, usr)
<span class="hljs-keyword">return</span> c.Next()

}

func currentUser(c *fiber.Ctx) models.User { usr := c.Locals(“user”).(models.User) return usr }

One advantage this method has over context values is that it’s possible to make them available to templates. This is done by enabling PassLocalsToViews in the app config:

app := fiber.New(fiber.Config{
    Views:             tmplEngine,
    PassLocalsToViews: true,
}) 

This makes it possible to load models using middleware, making them available to handlers that don’t operate on the model directly, yet still need to include it in the rendered template. A user object — usually fetched when verifying the authentication token — is a good example where this is useful, as you’re generally finding yourself needing to include the user’s username or email on the page in some way:

<nav>
  <a href="/user/details">{{.user.Email}}</a>
</nav>

Anyway, I feel like the last person to have learnt this, but it’s still a useful thing to learn.