go 46 lines · 9 steps

Per-user response caching in Gin with Redis

A Gin middleware that serves cached dashboards from Redis and captures fresh responses to populate the cache on a miss.

Explained by highlit
1func UserDashboardCache(rdb *redis.Client, ttl time.Duration) gin.HandlerFunc {
2 return func(c *gin.Context) {
3 claims, ok := c.Get("claims")
4 if !ok {
5 c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "missing claims"})
6 return
7 }
8 
9 user := claims.(*AuthClaims)
10 key := fmt.Sprintf("dashboard:v1:%s:%s", user.TenantID, user.Subject)
11 
12 if cached, err := rdb.Get(c.Request.Context(), key).Bytes(); err == nil {
13 c.Header("X-Cache", "HIT")
14 c.Data(http.StatusOK, "application/json; charset=utf-8", cached)
15 c.Abort()
16 return
17 }
18 
19 writer := &responseCapture{ResponseWriter: c.Writer, body: &bytes.Buffer{}}
20 c.Writer = writer
21 
22 c.Next()
23 
24 if writer.Status() == http.StatusOK && writer.body.Len() > 0 {
25 if err := rdb.Set(c.Request.Context(), key, writer.body.Bytes(), ttl).Err(); err != nil {
26 log.Printf("dashboard cache set failed for %s: %v", key, err)
27 }
28 }
29 writer.Header().Set("X-Cache", "MISS")
30 }
31}
32 
33type responseCapture struct {
34 gin.ResponseWriter
35 body *bytes.Buffer
36}
37 
38func (w *responseCapture) Write(b []byte) (int, error) {
39 w.body.Write(b)
40 return w.ResponseWriter.Write(b)
41}
42 
43func (w *responseCapture) WriteString(s string) (int, error) {
44 w.body.WriteString(s)
45 return w.ResponseWriter.WriteString(s)
46}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Middleware can short-circuit a request on a cache hit and let the handler run on a miss.
  2. 2Wrapping gin.ResponseWriter lets you tee the response body into a buffer for later reuse.
  3. 3Scoping cache keys by tenant and subject keeps cached data isolated per user.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Per-user response caching in Gin with Redis — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code