A cache is a hardware or software component that saves data so that subsequent requests for that data can be processed more quickly. Caching, at its most basic, refers to storing and retrieving data from a cache. It is an important concept that enables us to significantly increase the performance of an application.
Sometimes, an application may start to slow down due to the number of users, requests, or services. Caching offers a solution that might come in handy. There are several ways to implement in-memory caching in Go. This article will discuss how to implement in-memory caching using the go-cache package.
To understand in-memory caching using go-cache, we will build a simple web server using the HttpRouter package. This web server will demonstrate how and when to use the caching mechanism to increase the performance of our application.
Jump ahead:
The Replay is a weekly newsletter for dev and engineering leaders.
Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.
go-cache is an in-memory key:value store/cache that is similar to memcached and works well with applications that run on a single machine.
To install the go-cache and HttpRouter packages, run the following commands in your terminal:
go get github.com/patrickmn/go-cache go get github.com/julienschmidt/httprouter
Run the following commands to create a directory called caching:
mkdir caching cd caching
Next, we’ll enable dependency tracking with this command:
go mod init example/go_cache
Then, we’ll create a main.go file:
touch main.go
In main.go, the code will look like this:
package main
import (
"fmt"
"log"
"net/http"
"github.com/julienschmidt/httprouter"
)
func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprint(w, "Welcome!\n")
}
func main() {
router := httprouter.New()
router.GET("/", Index)
err := http.ListenAndServe(":8080", router)
if err != nil {
log.Fatal(err)
}
fmt.Println("Server running on :8080")
}
Finally, to start the server, run:
go run .
The server is running in Port 8080.
We have built a simple web server in Go, but it does nothing. Let’s make our server connect to an external API to query some data. This demonstrates how a web application often works. More often than not, a web app performs some network operations, highly computational tasks, and database queries.
https://fakestoreapi.com/ API provides us with a mock API. We’ll have to update the content of main.go to contain the following lines of code:
import (
...
"encoding/json"
"io"
)
type Product struct {
Price float64 `json:"price"`
ID int `json:"id"`
Title string `json:"title"`
Category string `json:"category"`
Description string `json:"description`
Image string `json:"image"`
}
func getProduct(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
id := p.ByName("id")
resp, err := http.Get("https://fakestoreapi.com/products/" + id)
if err != nil {
log.Fatal(err)
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
return
}
product := Product{}
parseErr := json.Unmarshal(body, &product)
if parseErr != nil {
log.Fatal(parseErr)
return
}
response, ok := json.Marshal(product)
if ok != nil {
log.Fatal("somethng went wrong")
}
w.Header().Set("Content-Type", "application/json")
w.Write(response)
}
func main() {
...
router.GET("/product/:id", getProduct)
...
}
Using the HttpRouter package, we create a product/:id endpoint that accepts a GET request. The router uses the getProduct function to handle the incoming requests to the endpoint.
Each network request will take a couple of milliseconds depending on how fast the user’s network connection is. Some requests might require high CPU usage. It is better to store the result of such requests in memory for quick retrieval, barring any updates to the underlying data, which might cause changes to the returned data.
The following code goes in the main.go file. We start by importing the go-cache package alongside the time package:
import (
...
"time"
"github.com/patrickmn/go-cache"
)
The code below helps to initialize the cache along with read and update methods that allow us to retrieve and input data to and from the cache:
type allCache struct {
products *cache.Cache
}
const (
defaultExpiration = 5 * time.Minute
purgeTime = 10 * time.Minute
)
func newCache() *allCache {
Cache := cache.New(defaultExpiration, purgeTime)
return &allCache{
products: Cache,
}
}
func (c *allCache) read(id string) (item []byte, ok bool) {
product, ok := c.products.Get(id)
if ok {
log.Println("from cache")
res, err := json.Marshal(product.(Product))
if err != nil {
log.Fatal("Error")
}
return res, true
}
return nil, false
}
func (c *allCache) update(id string, product Product) {
c.products.Set(id, product, cache.DefaultExpiration)
}
var c = newCache()
newCache invokes the cache.New() function, which creates a cache with a default expiration time of five minutes and purges expired items every 10 minutesread invokes the cache.Get(key) function, which retrieves an item with the given key. Type assertion is carried out on the retrieved item, so it can be passed to functions that don’t accept arbitrary types. The result is parsed to JSON format using the JSON.Marshal() functionupdate sets the value of the key id to product, with the default expiration timeAfter we’re done with the initialization of our cache, we have to think about how we want to implement the cache.
Usually, the approach is to check the cache for the requested resource. If it’s found in the cache, it’s returned to the client. However, if it’s not found, we proceed as usual to perform whatever action is needed to get the desired resource. Then the result is stored in the cache.
A programming concept that can help us perform the described action is middleware. A basic middleware in Go usually has this form:
func something(f http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Println("Request recieved")
f(w, r)
}
}
Following that pattern, we can create a checkCache middleware with HttpRouter:
func checkCache(f httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
id := p.ByName("id")
res, ok := c.read(id)
if ok {
w.Header().Set("Content-Type", "application/json")
w.Write(res)
return
}
log.Println("From Controller")
f(w, r, p)
}
}
The middleware here takes a httprouter.Handle as one of its parameters, wraps it, and returns a new httprouter.Handle for the server to call.
We call the c.read method with the id as its argument. If a product is found, we return that without proceeding any further.
We call the c.update method to save the retrieved product to the cache:
func getProduct(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
...
c.update(id, product)
w.Header().Set("Content-Type", "application/json")
w.Write(response)
}
Finally, we pass the getProduct function as an argument to the checkCache, therefore enabling the middleware on the endpoint. Requests to this endpoint will now make use of our cache:
func main() {
...
router.GET("/product/:id", checkCache(getProduct))
...
}
We’ve said that caching significantly improves the performance of our application. To support that claim, let’s perform some benchmarks.
The benchmarking tool of choice here is go-wrk, which you can install with this command:
go install github.com/tsliwowicz/go-wrk@latest
Next, we need to test our application with and without the caching middleware:
func main() {
...
router.GET("/product/:id", checkCache(getProduct))
...
}
With the caching middleware active, run:
go-wrk -c 80 -d 5 http://127.0.0.1:8080/product/1

Update the route to disable caching as follows:
func main() {
...
router.GET("/product/:id", getProduct)
...
}
Restart the server, then run the command below:
go-wrk -c 80 -d 5 http://127.0.0.1:8080/product/1

With 80 connections running for five seconds, we get the above results. The cached route could handle significantly more requests than the route that was not cached.
In this article, we discussed how to implement in-memory caching using the go-cache package. go-cache is just one of many packages available to handle in-memory caching in Go. Keep in mind that no matter the tool you choose to use, the underlying principle remains the same, and the benefits of caching are undoubtedly clear throughout the software development industry. It would be in your best interest to adopt the practice of caching in your next app or existing codebase.
Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, not
server-side
$ npm i --save logrocket
// Code:
import LogRocket from 'logrocket';
LogRocket.init('app/id');
// Add to your HTML:
<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>

Compare the top AI development tools and models of November 2025. View updated rankings, feature breakdowns, and find the best fit for you.

Discover what’s new in The Replay, LogRocket’s newsletter for dev and engineering leaders, in the November 5th issue.

A senior developer discusses how developer elitism breeds contempt and over-reliance on AI, and how you can avoid it in your own workplace.

Examine AgentKit, Open AI’s new tool for building agents. Conduct a side-by-side comparison with n8n by building AI agents with each tool.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up now