go 66 lines · 8 steps

Building a response cache middleware in Gin

A Gin middleware that serves cached JSON on a hit and captures fresh responses on a miss, all guarded by a read-write mutex.

Explained by highlit
1package middleware
2 
3import (
4 "bytes"
5 "net/http"
6 "sync"
7 "time"
8 
9 "github.com/gin-gonic/gin"
10)
11 
12type cacheEntry struct {
13 status int
14 body []byte
15 expiresAt time.Time
16}
17 
18type responseCache struct {
19 mu sync.RWMutex
20 entries map[string]cacheEntry
21}
22 
23var store = &responseCache{entries: make(map[string]cacheEntry)}
24 
25type capturingWriter struct {
26 gin.ResponseWriter
27 buf *bytes.Buffer
28}
29 
30func (w capturingWriter) Write(b []byte) (int, error) {
31 w.buf.Write(b)
32 return w.ResponseWriter.Write(b)
33}
34 
35func CacheJSON(ttl time.Duration) gin.HandlerFunc {
36 return func(c *gin.Context) {
37 key := c.Request.URL.RequestURI()
38 
39 store.mu.RLock()
40 entry, ok := store.entries[key]
41 store.mu.RUnlock()
42 
43 if ok && time.Now().Before(entry.expiresAt) {
44 c.Header("X-Cache", "HIT")
45 c.Data(entry.status, "application/json; charset=utf-8", entry.body)
46 c.Abort()
47 return
48 }
49 
50 writer := capturingWriter{ResponseWriter: c.Writer, buf: &bytes.Buffer{}}
51 c.Writer = writer
52 c.Header("X-Cache", "MISS")
53 
54 c.Next()
55 
56 if status := c.Writer.Status(); status == http.StatusOK && writer.buf.Len() > 0 {
57 store.mu.Lock()
58 store.entries[key] = cacheEntry{
59 status: status,
60 body: writer.buf.Bytes(),
61 expiresAt: time.Now().Add(ttl),
62 }
63 store.mu.Unlock()
64 }
65 }
66}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Embedding gin.ResponseWriter lets you intercept writes while delegating the rest of the interface for free.
  2. 2A sync.RWMutex lets many requests read the cache concurrently while serializing the rare write.
  3. 3Middleware can short-circuit with c.Abort() on a hit, or run c.Next() and inspect the result to populate the cache.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building a response cache middleware in Gin — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code